zoukankan      html  css  js  c++  java
  • 退役了退役了,把自己的板子放一下,需要的自取

    矩阵算法

    扫描线线段树

    单调栈

    扫描线

    int a[N][M], n, m;
    int l[N][N], r[N][N], h[N][N];//向左,右,上最远
    
    int read() {
        char c = getchar();
        while (c != '1' && c != '0')c = getchar();
        return c == '1'; //返回1代表障碍
    }
    
    //切记mr=m+1!!!!
    //遇到是障碍 ml=0,mr=m+1!!!!
    
    int main() {
        int ans = 0;
        scc(n, m);
        fir(i, 1, m) r[0][i] = m + 1;
        fir(i, 1, n) {
            int ml = 0, mr = m + 1;
            fir(j, 1, m) {
                a[i][j] = read();
                if (a[i][j]) ml = j, l[i][j] = 0, h[i][j] = 0;
                else l[i][j] = max(l[i - 1][j], ml), h[i][j] = h[i - 1][j] + 1;
            }
            fri(j, m, 1)
                if (a[i][j]) mr = j, r[i][j] = m + 1;
                else r[i][j] = min(r[i - 1][j], mr),
                    ans = max(ans, h[i][j] * (r[i][j] - l[i][j] - 1);
        }
        printf("%d", ans);
        return 0;
    }
    

    障碍法

    int n, m, s, ans = 0;
    PII a[N];
    
    //顺序不能改
    inline bool work(int &up, int &down, int i, int j, int v)
    {
        if (v * (down - up) <= ans) return 1;
        umax(ans, (down - up) * (abs(a[j].x - a[i].x)));
        if (a[j].y<up || a[j].y>down)return 0;
        //尽管不在上下界内,也要先算在返回,防止这个点不卡上下边界,但是最以后一个点而没算
        if (a[j].y == a[i].y)return 1;
        if (a[j].y < a[i].y)up = a[j].y;
        else down = a[j].y;
        return 0;
    }
    
    int main() {
        sccc(n, m, s); //n*m网格, s个障碍, 复杂度只和s有关(O(s^2))
        fir(i, 1, s) scc(a[i].fi, a[i].se);
        a[++s] = { 0, 0 }; a[++s] = { n, 0 };
        a[++s] = { 0, m }; a[++s] = { n, m };
        sort(a + 1, a + 1 + s);
        fir(i, 1, s) {
            RE int up = 0, down = m, v = n - a[i].fi;
            fir(j, i + 1, s) if (work(up, down, i, j, v)) break;
            up = 0, down = m, v = a[i].fi;
            fri(j, i - 1, 1) if (work(up, down, i, j, v)) break;
        }
        sort(a + 1, a + 1 + s, [&](PII x, PII y) {
            return x.se != y.se ? x.se < y.se : x.fi < y.fi;
        });
        fir(i, 1, s - 1) umax(ans,  m * (a[i + 1].se - a[i].se));
        printf("%d", ans);
    }
    

    数据结构

    并查集

    带权

    int n, f[N], dep[N], sz[N];
    int find(int x) {
        if (x == f[x]) return x;
        if (f[x] == f[f[x]]) return f[x];
        int fa = find(f[x]); dep[x] += dep[f[x]] - 1;
        return f[x] = fa;
    }
    void unit(int x, int y) {
        x = find(x), y = find(y);
        if (x != y) dep[y] = sz[x] + 1, sz[f[y] = x] += sz[y];
    }
    

    分块

    解决出现次数

    #pragma GCC optimize(2)
    const int N = 1e5 + 5, M = pow(N, 1.0 / 3) + 5;
    int n, m, k, cnt[M][N], val[M][M], t, len, a[N], id[N];
    vector<int> c;
    void init() {
        for (int i = 1; i <= n; ++i) cin >> a[i], c.push_back(a[i]);
        sort(all(c)); c.erase(unique(all(c)), c.end());
        t = pow(n, 1.0 / 3); len = (n - 1) / t + 1; t = (n - 1) / len + 1;
        rep(i, 1, n) a[i]=lower_bound(all(c),a[i])-c.begin(), id[i]=(i-1)/len+1;
        for (int i = t, res = 0; i; --i, res = 0) {
            for (int j = min(n, i * len); j; --j) {
                ++cnt[i][a[j]];
                if ((cnt[i][a[j]] & 1) && cnt[i][a[j]] - 1) --res;
                else if (cnt[i][a[j]] - 1) ++res;
                if (j % len == 1) val[id[j]][i] = res;
            }
        }
    }
    int work(int l, int L, int R, int r) {    int ans = val[id[L]][id[R]];
        for (int i = l; i < L; ++i) {
            ++cnt[id[L] - 1][a[i]];
            if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]] & 1) --ans;
            else if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]]) ++ans;
        }
        for (int i = r; i > R; --i) {
            --cnt[id[R]][a[i]];
            if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]] & 1) --ans;
            else if (cnt[id[R]][a[i]] - cnt[id[L] - 1][a[i]]) ++ans;
        }
        for (int i = l; i < L; ++i) --cnt[id[L] - 1][a[i]];
        for (int i = r; i > R; --i) ++cnt[id[R]][a[i]];
        return ans;
    }
    int main() {
        IOS; cin >> n >> k >> m; init();
        for (int i = 0, ls = 0; i < m; ++i) {
            int l, r; cin >> l >> r; l = (l + ls) % n + 1, r = (r + ls) % n + 1;
            if (l > r) swap(l, r);
            cout << (ls = work((id[l] - 1) * len + 1, l, r, min(id[r] * len, n))) << '
    ';
        }
        return 0;
    }
    

    树状数组

    区改单查

    int a[N], c[N];
    void add(int x, int k) { for (; x <= n; x += -x & x) c[x] += k; }
    void add(int l, int r, int k) { add(l, k); add(r + 1, -k); }
    int ask(int x) { int ans = 0; for (; x; x -= x & x) ans += c[x]; return ans; } //+a[x];
    

    区查区改

    int c[2][N], a[N], sum[N];
    void add(int x,ll k) {for(int i=x;i<=n;i+=-i&i) c[0][i] += k, c[1][i] += x * k; }
    void add(int l, int r, ll k) { add(l, k); add(r + 1, -k); }
    ll ask(int x) {
        ll p = 0, q = 0, f = x + 1;
        for (; x; x -= -x & x) p += c[0][x], q += c[1][x];
        return p * f - q;
    }
    ll ask(int l, int r) { return ask(r) - ask(l - 1); }//+sum[r] - sum[l - 1]
    

    二维区改单查

    ll c[N][M], a[N][M];
    void add(int x, int y, ll k){
        for (int t = y; x <= n; x += -x & x, y = t)
            for (; y <= m; y += -y & y) c[x][y] += k;
    }
    void add(int x1, int y1, int x2, int y2, ll k) { //左上角, 右下角
        add(x1, y1, k); add(x2 + 1, y2 + 1, k);
        add(x1, y2 + 1, -k); add(x2 + 1, y1, -k);
    }
    ll ask(int x, int y) {
        ll ans = 0;
        for (int t = y; x <= n; x -= -x & x, y = t)
            for (; y <= m; y -= -y & y) ans += c[x][y];
        return ans;
    }
    

    二维区查区改

    ll c[4][N][M], a[N][M], sum[N][M];
    void add(int x, int y, ll k) {
        for (int i = x; i <= n; i += -i & i)
            for (int j = y; j <= m; j += -j & j) {
                c[0][i][j] += k;
                c[1][i][j] += k * x;
                c[2][i][j] += k * y;
                c[3][i][j] += k * x * y;
            }
    }
    void add(int x1, int y1, int x2, int y2, ll k) {
        add(x1, y1, k); add(x2 + 1, y2 + 1, k);
        add(x1, y2 + 1, -k); add(x2 + 1, y1, -k);
    }
    ll ask(int x, int y) {
        ll p = 0, q = 0, r = 0, t = 0;
        for (int i = x; i; i -= -i & i)
            for (int j = y; j; j -= -j & j)
                p += c[0][i][j], q += c[1][i][j], r += c[2][i][j], t += c[3][i][j];
        return p * (x + 1) * (y + 1) - q * (y + 1) - r * (x + 1) + t;
    }
    ll ask(int x1, int y1, int x2, int y2) {
        return ask(x2, y2) - ask(x1 - 1, y2) - ask(x2, y1 - 1) + ask(x1 - 1, y1 - 1);
    }
    

    线段树

    基本(区间和)

    struct BitTree {
        struct node { int l, r, val, tag, len; } tr[N << 2];
        void push_up(int rt) { tr[rt].val = tr[rt<<1].val + tr[rt<<1|1].val; }
        void push_down(int rt) {
            tr[rt << 1].tag += tr[rt].tag, tr[rt << 1 | 1].tag += tr[rt].tag;
            tr[rt << 1].val += tr[rt << 1].len * tr[rt].tag;
            tr[rt << 1 | 1].val += tr[rt << 1 | 1].len * tr[rt].tag;
            tr[rt].tag = 0;
        }
        void build(int rt, int l, int r, int* a) {
            tr[rt] = {l, r, 0, 0, r - l + 1};
            if (l == r) { tr[rt].val = a[l]; return; }
            int mid = l + r >> 1;
            build(rt << 1, l, mid, a); build(rt << 1 | 1, mid + 1, r, a);
            push_up(rt);
        }
        void change(int rt, int l, int r, int k) {
            if (r < l) return;
            if (tr[rt].l >= l && tr[rt].r <= r) {
                tr[rt].val += tr[rt].len * k;
                tr[rt].tag += k; return;
            } push_down(rt);
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (l <= mid) change(rt << 1, l, r, k);
            if (r > mid) change(rt << 1 | 1, l, r, k);
            push_up(rt);
        }
        ll ask(int rt, int l, int r) {
            if (r < l) return 0;
            if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt].val;
            push_down(rt);
            int mid = tr[rt].l + tr[rt].r >> 1;
            ll ans = l <= mid ? ask(rt << 1, l, r) : 0;
            if (r > mid) ans += ask(rt << 1 | 1, l, r);
            push_up(rt); return ans;
        }
    } bit;
    

    扫描线面积(计算覆盖k次的面积)

    struct BIT { //总的覆盖k次以上的长度为tr[1].val[k - 1]
        static const int N = 2e3 + 5;
        struct Node {
                    int l, r, len, val[3], cnt;//val[i]代表扫描i-1次的线段长度,cnt为覆盖次数
            int& operator [](int k) { return val[k]; }
        } tr[N << 2];
        void push_up(int rt) {
            rep (i, 0, 2)//rep(i, 0, k - 1)
                if (tr[rt].cnt > i) tr[rt][i] = tr[rt].len;
                else if (tr[rt].l + 1 == tr[rt].r) tr[rt][i] = 0;
                else tr[rt][i] = tr[rt<<1][i - tr[rt].cnt] + tr[rt<<1|1][i - tr[rt].cnt];
        }
        void build(int rt, int l, int r, VI& a) {//a为离散化的x vector
            tr[rt].l = l, tr[rt].r = r, tr[rt].len = a[r] - a[l];
            tr[rt][0] = tr[rt][1] = tr[rt][2] = tr[rt].cnt = 0;
            if (l + 1 == r) return;
            int mid = l + r >> 1;
            build(rt << 1, l, mid, a); build(rt << 1 | 1, mid, r, a);
        }
        void change(int rt, int l, int r, int k) {
            if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].cnt += k; push_up(rt); return; }
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (mid > l) change(rt << 1, l, r, k);
            if (mid < r) change(rt << 1 | 1, l, r, k);
            push_up(rt);
        }
        int ask(int rt, int l, int r, int k) {//覆盖k次以上的长度和
            if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt][k];
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (r <= mid) return ask(rt << 1, l, r, k);
            if (l >= mid) return ask(rt << 1 | 1, l, r, k);
            return ask(rt << 1, l, r, k) + ask(tr << 1 | 1, l, r, k);
        }
    } bit;
    

    扫描线周长

    struct BIT {
        static const int N = 1e4 + 5;
        struct node { int l, r, len, cnt, tag, val[2]; } tr[N << 2];
        void push(int rt) {
            if (tr[rt].tag) tr[rt].val[0] = tr[rt].val[1] = tr[rt].len, tr[rt].cnt = 1;
            else if (tr[rt].l + 1 == tr[rt].r)
                tr[rt].val[0] = tr[rt].val[1] = 0, tr[rt].cnt = 0;
            else {
                tr[rt].cnt = tr[rt << 1].cnt + tr[rt << 1 | 1].cnt -
                    (tr[rt << 1].val[1] && tr[rt << 1 | 1].val[0] ? 1 : 0);
                tr[rt].val[0] = tr[rt << 1].val[0] +
                    (tr[rt << 1].val[0] ^ tr[rt << 1].len ? 0 : tr[rt << 1 | 1].val[0]);
                tr[rt].val[1] = tr[rt << 1 | 1].val[1] +
                    (tr[rt << 1 | 1].val[1] ^ tr[rt << 1 | 1].len ? 0 : tr[rt << 1].val[1]);
             }
        }
        void build(int rt, int l, int r, vector<int>& a) {
            tr[rt].l = l, tr[rt].r = r, tr[rt].len = a[r] - a[l];
            if (l + 1 == r) return;
            int mid = l + r >> 1; build(rt << 1, l, mid, a); build(rt << 1 | 1, mid, r, a);
        }
        void change(int rt, int l, int r, int k) {
            if (tr[rt].l >= l && tr[rt].r <= r) { tr[rt].tag += k; push(rt); return; }
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (l < mid) change(rt << 1, l, r, k);
            if (r > mid) change(rt << 1 | 1, l, r, k);
            push(rt);
        }
    } bit;
    struct line { int y, a, b, k; } a[10005];
    int n, x[10000], y[10000];
    long long work(int x[], int y[]) {
        vector<int> c; c.push_back(-2e9);
        for (int i = 0; i < n; ++i) {
            a[i << 1] = { y[i << 1], x[i << 1], x[i << 1 | 1], 1 };
            a[i << 1 | 1] = { y[i << 1 | 1], x[i << 1], x[i << 1 | 1], -1 };
            c.push_back(x[i << 1]); c.push_back(x[i << 1 | 1]);
        }
        sort(c.begin(), c.end()); c.erase(unique(c.begin(), c.end()), c.end());
        sort(a, a + n * 2, [](line& a, line& b){return a.y^b.y?a.y<b.y:a.k>b.k;});
        long long ans = 0; bit.build(1, 1, c.size() - 1, c);
        bit.change(1, lower_bound(c.begin(), c.end(), a[0].a) - c.begin(),
            lower_bound(c.begin(), c.end(), a[0].b) - c.begin(), a[0].k);
        for (int i = 1; i < n << 1; ++i) {
            ans += (long long)bit.tr[1].cnt * (a[i].y - a[i - 1].y) << 1;
            bit.change(1, lower_bound(c.begin(), c.end(), a[i].a) - c.begin(),
                lower_bound(c.begin(), c.end(), a[i].b) - c.begin(), a[i].k);
        }
        return ans;
    }
    int main() {
        cin >> n;
        for (int i = 0; i < n; ++i) cin>>x[i<<1]>>y[i<<1]>>x[i<<1|1]>>y[i<<1|1];
        cout << work(x, y) + work(y, x);
        return 0;
    }
    

    主席树

    可持续线段树

    struct CHT {
        static const int N = 1e5 + 5;
        struct Node { int l, r; ll val, tag; } tr[N * 25];
        int rt[N], tot; // 不可push_up/down
        void build(int& rt, int l, int r, ll* a) {
            tr[rt = ++tot] = { 0, 0, 0, 0 };
            if (l == r) { tr[rt].val = a[l]; return; }
            int mid = l + r >> 1;
            build(tr[rt].l, l, mid, a); build(tr[rt].r, mid + 1, r, a);
            tr[rt].val = tr[tr[rt].l].val + tr[tr[rt].r].val
        }
        void change(int& x, int y, int pl, int pr, int l, int r, ll k) {
            tr[x = ++tot] = tr[y];
            tr[x].val += (min(r, pr) - max(pl, l) + 1) * k;
            if (l <= pl && pr <= r) { tr[x].tag += k; return; }
            int mid = pl + pr >> 1;
            if (l <= mid) change(tr[x].l, tr[y].l, pl, mid, l, r, k);
            if (r > mid) change(tr[x].r, tr[y].r, mid + 1, pr, l, r, k);
        }
        ll ask(int rt, int pl, int pr, int l, int r) {
            if (l <= pl && pr <= r) return tr[rt].val;
            ll ans = (min(pr, r) - max(pl, l) + 1) * tr[rt].tag;
            int mid = pl + pr >> 1;
            if (mid >= l) ans += ask(tr[rt].l, pl, mid, l, r);
            if (mid < r) ans += ask(tr[rt].r, mid + 1, pr, l, r);
            return ans;
        }
    } T;
    

    静态

    struct CHT {
        struct node { int val, lson, rson; } tr[20 * N]; //log2(sz)*n,sz值域
        int root[N], tot;
        /*void push_up(int rt) { tr[rt].val = tr[tr[rt].l].val + tr[tr[rt].r].val; }
        //这样build tr 要 (log2(n) + 4) * n
        void build(int &rt, int l, int r, int countofa[]) {
            rt = ++tot; tr[rt].l = l, tr[rt].r = r;
            if (l == r) { tr[rt].val = countofa[l]; return; }
            int mid = l + r >> 1;
            build(tr[rt].lson, l, mid, countofa);
            build(tr[rt].rson, mid + 1, r, countofa);
            push_up(rt);
        } */
        void update(int &x, int y, int l, int r, int k, int cnt) {
            tr[x = ++tot] = tr[y]; tr[x].val += cnt;
            if (l == r) return;
            int mid = l + r >> 1;
            if (k <= mid) update(tr[x].lson, tr[y].lson, l, mid, k, cnt);
            else update(tr[x].rson, tr[y].rson, mid + 1, r, k, cnt);
        }
        int ask(int x, int y, int l, int r, int k) {
            if (l == r) return l;
            int mid = l + r >> 1, val = tr[tr[x].lson].val - tr[tr[y].lson].val;
            if (val >= k) return ask(tr[x].lson, tr[y].lson, l, mid, k);
            else return ask(tr[x].rson, tr[y].rson, mid + 1, r, k - val);
        }
    };
    

    动态

    const int N = 1e5 + 5;
    struct CHT {
        struct node { int val, lson, rson; } tr[N * 300]; //log2(sz)*log2(n)*n
        int root[N], tot, lsc, rsc, sz, n; // n为数组大小,sc为值域
        int ls[20], rs[20]; //log(n)
        void build(int n, int* a, int sz) {
            this->sz = sz; this->n = n;
            for (int i = 1; i <= n; ++i) change(i, a[i], 1);
        }
        void update(int &rt, int l, int r, int k, int cnt) {
            if (!rt) rt = ++tot;
            tr[rt].val += cnt;
            if (l == r) return;
            int mid = l + r >> 1;
            if (k <= mid) update(tr[rt].lson, l, mid, k, cnt);
            else update(tr[rt].rson, mid + 1, r, k, cnt);
        }
        void change(int x, int k, int cnt) { //n为最大区间长度
            for (int i = x; i <= n; i += i & -i) update(root[i], 1, sz, k, cnt);
        }
        int ask(int l, int r, int k) {
            if (l == r) return l;
            int sum = 0;
            for (int i = 1; i <= lsc; ++i) sum -= tr[tr[ls[i]].lson].val;
            for (int i = 1; i <= rsc; ++i) sum += tr[tr[rs[i]].lson].val;
            int mid = l + r >> 1;
            if (sum >= k) {
                for (int i = 1; i <= lsc; ++i) ls[i] = tr[ls[i]].lson;
                for (int i = 1; i <= rsc; ++i) rs[i] = tr[rs[i]].lson;
                return ask(l, mid, k);
            } else {
                for (int i = 1; i <= lsc; ++i) ls[i] = tr[ls[i]].rson;
                for (int i = 1; i <= rsc; ++i) rs[i] = tr[rs[i]].rson;
                return ask(mid + 1, r, k - sum);
            }
        }
        int preask(int l, int r, int k) {
            lsc = rsc = 0;
            for (int i = l; i; i -= -i & i) ls[++lsc] = root[i];
            for (int i = r; i; i -= -i & i) rs[++rsc] = root[i];
            return ask(1, sz, k);
        }
    } bit;
    cout << c[bit.preask(q[i].l - 1, q[i].r, q[i].k)] << '
    ';
    bit.change(q[i].l, a[q[i].l], -1);
    a[q[i].l] = lower_bound(all(c), q[i].k) - c.begin();
    bit.change(q[i].l, a[q[i].l], 1);
    

    树上动态第k大,通过dfs序维护

    struct CHT {
        static const int M = 6e7;
        struct node {
            int val, lson, rson; /*int l, r; 值域*/
            node (int Val = 0, int Ls = 0, int Rs = 0)
                : val(Val), lson(Ls), rson(Rs){}
        } tr[N* 520]; //log2(sz)*log2(sz)*n
        int root[N], tot, qsc, qtc, quc, qwc, sz, n; // n为数组大小,sc为值域
        int qs[15], qt[15], qu[15], qw[15]; //log(n)
        void build(int n, int sz, int *a, PII *dfn) {
            this->sz = sz; this->n = n;
            for (int i = 1; i <= n; ++i)
                change(dfn[i].fi, a[i], 1), change(dfn[i].se + 1, a[i], -1);
        }
        void update(int &rt, int l, int r, int k, int cnt) {
            if (!rt) rt = ++tot;
            tr[rt].val += cnt;
            if (l == r) return;
            int mid = l + r >> 1;
            if (k <= mid) update(tr[rt].lson, l, mid, k, cnt);
            else update(tr[rt].rson, mid + 1, r, k, cnt);
        }
        void change(int x, int k, int cnt) { //n为最大区间长度
            for (int i = x; i <= n; i += i & -i) update(root[i], 0, sz, k, cnt);
        }
        int ask(int l, int r, int k) {
            if (l == r) return l;
            int sum = 0;
            for (int i = 1; i <= qsc; ++i) sum += tr[tr[qs[i]].lson].val;
            for (int i = 1; i <= qtc; ++i) sum += tr[tr[qt[i]].lson].val;
            for (int i = 1; i <= quc; ++i) sum -= tr[tr[qu[i]].lson].val;
            for (int i = 1; i <= qwc; ++i) sum -= tr[tr[qw[i]].lson].val;
            int mid = l + r >> 1;
            if (sum >= k) {
                for (int i = 1; i <= qsc; ++i) qs[i] = tr[qs[i]].lson;
                for (int i = 1; i <= qtc; ++i) qt[i] = tr[qt[i]].lson;
                for (int i = 1; i <= quc; ++i) qu[i] = tr[qu[i]].lson;
                for (int i = 1; i <= qwc; ++i) qw[i] = tr[qw[i]].lson;
                return ask(l, mid, k);
            } else {
                for (int i = 1; i <= qsc; ++i) qs[i] = tr[qs[i]].rson;
                for (int i = 1; i <= qtc; ++i) qt[i] = tr[qt[i]].rson;
                for (int i = 1; i <= quc; ++i) qu[i] = tr[qu[i]].rson;
                for (int i = 1; i <= qwc; ++i) qw[i] = tr[qw[i]].rson;
                return ask(mid + 1, r, k - sum);
            }
        }
        int preask(int s, int t, int u, int w, int k) {
            qtc = qsc = quc = qwc = 0;
            for (int i = s; i; i -= -i & i) qs[++qsc] = root[i];
            for (int i = t; i; i -= -i & i) qt[++qtc] = root[i];
            for (int i = u; i; i -= -i & i) qu[++quc] = root[i];
            for (int i = w; i; i -= -i & i) qw[++qwc] = root[i];
            return ask(0, sz, k);
        }
    } bit;
    int n, m, _, k;
    int h[N], ne[N << 1], to[N << 1], tot;
    int op[N], x[N], y[N], a[N], df;
    PII dfn[N];
    void add(int u, int v) { ne[++tot] = h[u]; to[h[u] = tot] = v;}
    void dfs(int x, int f) {
        dfn[x].fi = ++df;
        for (int i = h[x]; i; i = ne[i]) {
            int y = to[i];
            if (y == f) continue;
            dfs(y, x);
        } dfn[x].se = df;
    }
    int main() {//op==0,表a[x]修改为y,其他表示求(x,y)简单路径第op大
        IOS; cin >> n >> m; VI c;
        rep (i, 1, n) cin >> a[i], c.pb(a[i]);
        rep (i, 2, n) {
            int u, v; cin >> u >> v;
            add(u, v); add(v, u);
        }
        rep (i, 1, m) {
            cin >> op[i] >> x[i] >> y[i];
            if (!op[i]) c.pb(y[i]);
        }
        sort(all(c)); c.erase(unique(all(c)), c.end());
        rep (i, 1, n) a[i] = lower_bound(all(c), a[i]) - c.begin();
        rep (i, 1, m) if (!op[i]) y[i] = lower_bound(all(c), y[i]) - c.begin();
        ST.init(n, h, ne, to); ST.bfs(1);//st表,求lca的轮子
        dfs(1, 0); bit.build(n, c.size(), a, dfn);
        rep (i, 1, m) {
            if (!op[i]) {
                bit.change(dfn[x[i]].fi, a[x[i]], -1);
                bit.change(dfn[x[i]].se + 1, a[x[i]], 1);
                a[x[i]] = y[i];
                bit.change(dfn[x[i]].fi, a[x[i]], 1);
                bit.change(dfn[x[i]].se + 1, a[x[i]], -1);
            } else {
                int k = ST.dist(x[i], y[i]) + 2 - op[i], lca = ST.lca(x[i], y[i]);
                if (k <= 0) cout << "invalid request!
    ";
                else cout << c[bit.preask(dfn[x[i]].fi, dfn[y[i]].fi,
                    dfn[lca].fi, dfn[ST.f[lca][0]].fi, k)] << '
    ';
            }
        }
        return 0;
    }
    

    可持续化01trie

    1、在序列末尾添加一个数 x。
    2、找到一个位置 p,满足l≤p≤r,a[p~N]异或和 xor x 最大,输出这个最大值。
    即 a[1]~a[n] 异或 (l-1r-1)a[1]a[i]与x的异或和最大

    struct SustainableTrie {
        const int N = 2e4 + 5, M = 31;
        struct node {
            int son[2], cnt;
            int& operator [](const int x) { return son[x]; }
        } tr[N * M];
        int rt[N], tot;
        void init() {
            for (int i = 1; i <= tot; ++i) rt[i] = 0; tot = 0;
            for (int i = 0; i < M; ++i) tr[0][i] = 0;
        }
        void insert(int& x, int y, int k) {
            int p = x = ++tot; tr[p] = tr[y];
            for (int i = M - 1; ~i; --i) {
                int ch = k >> i & 1;
                tr[p = tr[p][ch] = ++tot] = tr[y = tr[y][ch]];
                ++tr[p].cnt;
            }
        }
        int ask(int x, int y, int k) {
            int ans = 0;
            for (int i = M - 1; ~i; --i) {
                int ch = k >> i & 1;
                if (tr[tr[x][!ch]].cnt - tr[tr[y][!ch]].cnt)
                    ans ^= 1ll << i, ch = !ch;
                x = tr[x][ch], y = tr[y][ch];
            } return ans;
        }
    } trie;
    int n, m, _, k, s;
    int main() {
        IOS; cin >> n >> m; k = 1; trie.insert(trie.rt[k], trie.rt[0], 0);
        rep (i, 1, n) cin >> _, ++k, trie.insert(trie.rt[k], trie.rt[k - 1], s ^= _);
        rep (i, 1, m) {
            char op[2]; cin >> op;
            if (op[0] == 'A') cin>>_,++k,trie.insert(trie.rt[k],trie.rt[k - 1],s^=_);
            else {
                int l, r, x; cin >> l >> r >> x;
                cout << trie.ask(trie.rt[r], trie.rt[l - 1], x ^ s) << '
    ';
            }
        } return 0;
    }
    void work(int x) {
        int tail = 0; dis[x] = { 0, 0 }; q[++tail] = x; v[f[x] = x] = 1;
        /*for (auto& y : h[x]) if (!v[y.first] && y.second <= k)
            dis[q[++tail]=y.first]={y.second,1},dfs(y.first,x,f[y.first]=y.first,tail);
        sort(q + 1, q + tail + 1, [&](int a, int b) { return dis[a] < dis[b]; });
        for (int l = 1, r = 1; l <= tail; ++l) {
            while (l < r - 1 && dis[q[l]].first + dis[q[r - 1]].first >= k) --r;
            while (r < tail && dis[q[l]].first + dis[q[r]].first < k) ++r;
            for (; r <= tail && dis[q[l]].first + dis[q[r]].first == k; ++r)
                if (f[q[l]] ^ f[q[r]]) ans = min(ans, dis[q[l]].second + dis[q[r]].second);
        }遍历完孩子在计算*/
        unordered_map<int, int> st; st[0] = 0;//st可换成桶,循环完把更新过的位置重新设max即可
        for (auto& y : h[x]) if (!v[y.first] && y.second <= k) {
            dis[q[++tail] = y.first] = { y.second, 1 }; q[tail = 1] = y.first;
            dfs(y.first, x, f[y.first] = y.first, tail);
            for (int i = 1; i <= tail; ++i) if (st.count(k - dis[q[i]].first))
                ans = min(ans, st[k - dis[q[i]].first] + dis[q[i]].second);
            for (int i = 1; i <= tail; ++i) {
                auto it = st.find(dis[q[i]].first);
                if (it == st.end()) st.insert(dis[q[i]]);
                else it->second = min(it->second, dis[q[i]].second);
            }
        } //直接一边遍历一便计算, 省去f[]数组
        gravity(x, -1, n); mxsz = n;
        for(auto&y:h[x])if(!v[y.first])gravity(y.first,-1,sz[y.first]),work(gra),mxsz=n;
    }
    int main() {
        IOS; cin >> n >> k; mxsz = ans = n;
        for (int i = 1; i < n; ++i) {
            int u, v, c; cin >> u >> v >> c;
            h[u].push_back({ v, c }); h[v].push_back({ u, c });
        }
        gravity(0, -1, n); work(gra); cout << (ans ^ n ? ans : -1);
        return 0;
    }
    

    点分治

    1.先找当前子树的重心, 别走出当前子树
    2.在把重心当成当前子树的根, 开始计算
    3.对当前子树根节点(重心)的儿子节点进行1~3

    带权树, 求简单路径长度为k, 所包含最少边的数量

    #pragma GCC optimize(2)
    int n, k, gra, sz[N], mxsz, q[N], f[N], ans;
    bool v[N];
    pair<int, int> dis[N];
    vector<pair<int, int>> h[N];
    void gravity(int x, int fa, int s) {
        sz[x] = 1; int mx = 0;
        for (auto& y : h[x]) if (y.first != fa && !v[y.first])
            gravity(y.first, x, s), sz[x] += sz[y.first], mx = max(mx, sz[y.first]);
        if ((mx = max(mx, s - sz[x])) < mxsz) gra = x, mxsz = mx;
    }
    void dfs(int x, int fa, int id, int& tail) {
        for (auto& y : h[x]) if (y.first != fa && !v[y.first]) {
            f[y.first] = id; dis[q[++tail] = y.first].second = dis[x].second + 1;
            dis[y.first].first = dis[x].first + y.second;
            if (dis[y.first].first < k) dfs(y.first, x, id, tail);
        }
    }
    

    cdq 分治

    三维偏序

    第一行包含两个整数n和m,在刚开始时,Ayu已经知道有n个点可能埋着天使玩偶,接下来Ayu要进行m次操作。
    接下来n行,每行两个非负整数xi,yi,表示初始n个点的坐标。
    再接下来m行,每行三个非负整数 t,x,y 。
    如果t=1,表示Ayu又回忆起了一个可能埋着玩偶的点(x,y)。
    如果t=2,表示Ayu询问如果她在坐标(x,y),那么在已经回忆出的点里,离她最近的那个点有多远(曼哈顿)。

    这题异常卡常, 开氧气, 最好再来快读
    直接拆只考虑 (|x-x_i|+|y-y_i|=(x+y)-(x_i+y_i))
    旋转四次,cdq分治即可
    直接拿时间轴当一维(直接有序)
    二维偏序 x轴, 树状维护 y轴信息(还不用离散化)
    记得树状恢复的技巧

    #pragma GCC optimize(2)
    template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b?(a = b, true):false; }
    template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b?(a = b, true):false; }
    template<class T> void clear(T& a) { T().swap(a); }
    const int N = 5e5 + 5, M = 1e6 + 5;
    int n, m, _, k;
    struct node { int op, x, y, t; } q[N << 1], a[N << 1], b[N << 1];
    int tot, ans[N << 1], c[M], mxx, mxy;
    void add(int x, int k) { for (; x <= mxy; x += -x & x) umax(c[x], k); }
    int ask(int x){int ans=0;for(;x;x-=-x&x) umax(ans,c[x]); return ans?ans:-1e7; }
    void cl(int x) { for (; x <= mxy; x += -x & x) c[x] = 0; }
    void init(int k) {
        int x = 0, y = 0; tot = 0;
        rep(i, 1, n + m) {
            if (k == 1) q[i].x = mxx - q[i].x + 1;
            else if (k == 2) q[i].y = mxy - q[i].y + 1;
            if (q[i].op == 2) umax(x, q[i].x), umax(y, q[i].y);
        }
        rep(i, 1, n + m) if (q[i].x <= x && q[i].y <= y) a[++tot] = q[i];
    }
    void cdq(int l, int r) {
        if (l == r) return;
        int mid = l + r >> 1;
        cdq(l, mid); cdq(mid + 1, r);
        int x = l, y = mid + 1, z = l;
        for (; y <= r; b[z++] = a[y++]) {
            for (; x <= mid && a[x].x <= a[y].x; b[z++] = a[x++])
                if (a[x].op == 1) add(a[x].y, a[x].x + a[x].y);
            if (a[y].op == 2) umin(ans[a[y].t], a[y].y + a[y].x - ask(a[y].y));
        }
        rep (i, l, x - 1) if (a[i].op == 1) cl(a[i].y);
        rep (i, x, mid) b[z++] = a[i];
        rep (i, l, r) a[i] = b[i];
    }
    int main() {
        IOS; cin >> n >> m; VI idx;
        rep(i, 1, n) {
            cin >> q[i].x >> q[i].y, q[i].t = i; q[i].op = 1;
            umax(mxx, ++q[i].x); umax(mxy, ++q[i].y);
        }
        rep(i, n + 1, n + m) {
            cin >> q[i].op >> q[i].x >> q[i].y; q[i].t = i;
            umax(mxx, ++q[i].x); umax(mxy, ++q[i].y); ans[i] = 1e9;
            if (q[i].op == 2) idx.pb(q[i].t);
        }
        init(0); cdq(1, tot); init(1); cdq(1, tot);
        init(2); cdq(1, tot); init(1); cdq(1, tot);
        for (auto i : idx) cout << ans[i] << '
    ';
        return 0;
    }
    

    整体二分

    一个环,分成m个区间,每个分别属于一个国家,k个运算,使得[l,r] ([l,m],[1,r])加x,
    求每个国家达到自己要求的a[i]最少要经过几次运算(运算顺序不可改),不存在NIE
    可用区间树状,也可以用差分(区间修改,单点询问), 板子用的前面的区间改查

    struct node { ll op, l, r, id; } q[N << 1], qu[N << 1];
    int n, m, _, k;
    ll ans[N], c[2][N];
    VI a[N];
    void solve(int l, int r, int ql, int qr) {
        if (ql > qr) return;
        if (l == r) {
            rep(i, ql, qr) if (q[i].op == 0) ans[q[i].id] = l;
            return;
        }
        int mid = l + r >> 1, nl = ql - 1, nr = 0;
        rep(i, ql, qr)
            if (q[i].op) {
                if (q[i].id > mid) { qu[++nr] = q[i]; continue; }
                if (q[i].l <= q[i].r) add(q[i].l, q[i].r, q[i].op);
                else add(q[i].l, m, q[i].op), add(1, q[i].r, q[i].op);
                //add(m + 1, -q[i].op) m+1并不会执行, 就省略了
                q[++nl] = q[i];
            } else {
                __int128 cur = 0;//3e5*3e5*1e9 爆ll
                for (auto j : a[q[i].id]) cur += ask(j, j);
                if (cur >= q[i].l) q[++nl] = q[i];
                else q[i].l -= cur, qu[++nr] = q[i];
            }
        rep(i, ql, nl) if (q[i].op)
            if (q[i].l <= q[i].r) add(q[i].l, q[i].r, -q[i].op);
            else add(q[i].l, m, -q[i].op), add(1, q[i].r, -q[i].op);
        rep(i, 1, nr) q[i + nl] = qu[i];
        solve(l, mid, ql, nl); solve(mid + 1, r, nl + 1, qr);
    }
    int main() {
        IOS; cin >> n >> m;
        rep(i, 1, m) cin >> _, a[_].pb(i);
        rep(i, 1, n) cin >> qu[i].l, qu[i].op = 0, qu[i].id = i;
        cin >> k;
        rep(i, 1, k) cin >> q[i].l >> q[i].r >> q[i].op, q[i].id = i;
        ++k; q[k] = { (int)2e9, 1, m, k };
        rep(i, 1, n) q[k + i] = qu[i];
        solve(1, k, 1, k + n);
        rep(i, 1, n) if (ans[i] != k) cout << ans[i] << '
    '; else cout << "NIE
    ";
        return 0;
    }
    

    可持续化并查集

    ll n,m;
    struct Lasting_Segment_Tree {
        struct node { ll lson, rson fa, dep; } t[MAXN << 5 | 1];
        ll cnt;
        Lasting_Segment_Tree() { cnt=0; }
        #define rt t[num]
        void build(ll& num, un l = 1, un r = n) {
            num=++cnt;
            if (l==r)rt.fa=l,rt.dep=1;
            else {
                un mid=(l+r)>>1;
                build(rt.lson,l,mid);
                build(rt.rson,mid+1,r);
            }
        }
        void modify(ll& num, ll pre, un pos, ll fa, un l = 1, un r = n) {
        //现在节点是num,前一个版本的现在节点是pre,要将pos位置的fa改为fa
            num = ++cnt; rt=t[pre];
            if(l == r) { rt.fa=fa; return;}
            un mid=(l+r)>>1;
            if(pos<=mid)modify(rt.lson,t[pre].lson,pos,fa,l,mid);
            else modify(rt.rson,t[pre].rson,pos,fa,mid+1,r);
        }
        void add(ll num, un pos, un l = 1, un r = n) {
        //现在节点是num,要讲pos位置深度+1
            if(l==r) { ++rt.dep; return;}
            un mid=(l+r)>>1;
            if(pos<=mid)add(rt.lson,pos,l,mid);
            else add(rt.rson,pos,mid+1,r);
        }
        pll Query(ll num,un pos,un l = 1,un r = n) {
        //返回pos位置的fa和dep
            if (l == r)return pll(rt.fa,rt.dep);
            un mid = (l + r) >> 1;
            if(pos<=mid)return Query(rt.lson,pos,l,mid);
            else return Query(rt.rson,pos,mid+1,r);
        }
    }sgt;
    ll root[MAXN];//每个版本的根节点
    pll find(ll w,ll x) {
    //找第w个版本下x的根和深度
        pll tmp=sgt.Query(root[w],x);
        if (tmp.first == x)return tmp;
        return find(w, tmp.first);//不能路径压缩!
    }
    int main() {
        n = read(), m = read(); ll lastans=0;
        sgt.build(root[0]);
        for(ll i = 1, op = read(); i <= m; ++i) {
            root[i] = root[i-1];//复制
            if(op==1) {
                pll x, y;
                x = find(i, read() ^ lastans); y = find(i, read() ^ lastans);
                if (x.first == y.first) continue;
                if (x.second > y.second) std::swap(x,y);
                sgt.modify(root[i], root[i-1], x.first, y.first);
                if (x.second == y.second) sgt.add(root[i], y.first);
            } else if(op == 2) root[i] = root[read() ^ lastans];
            else {
                ll x = read() ^ lastans, y = read() ^ lastans;
                lastans = (find(i, x).first == find(i, y).first);
                printf("%d
    ", lastans);
            }
        }
        return 0;
    }
    

    splay

    struct Splay {
        static const int N = 1e5 + 5;
        struct Node {
            int fa, val, siz, cnt, l, r, ch[2];
            Node(int Fa = 0, int Val = 0, int Siz = 0, int L = 0, int R = 0):
                fa(Fa), val(Val), siz(Siz), cnt(Siz) { ch[0] = L, ch[1] = R; }
        } t[N];
        int root, tot;
        int getroot() { return t[root].val; }
        bool son(int p) {return p == t[t[p].fa].ch[1]; }
        int newNode(int fa=0,int v=0,int cnt=1){return t[++tot]=Node(fa,v,cnt),tot;}
        void connect(int p, int q, int k) { if (p) t[p].fa = q; t[q].ch[k] = p; }
        void push_up(int p) { t[p].siz = t[t[p].ch[0]].siz + t[t[p].ch[1]].siz + t[p].cnt; }
        void rotate(int p) {
            //if (root == p) return;
            int f = t[p].fa, g = t[f].fa; bool q1 = son(p), q2 = son(f);
            connect(t[p].ch[q1 ^ 1], f, q1); connect(f, p, q1 ^ 1);
            connect(p, g, q2); push_up(f); push_up(p);
        }
        void splay(int p, int q) {
            while (t[p].fa != q) {
                int f = t[p].fa, g = t[f].fa;
                if (g != q) rotate(son(p) ^ son(f) ? p : f);
                rotate(p);
            } if (q == 0) root = p;
        }
        void insert(int x) {
            int p = root, fa = 0;
            for (; p && t[p].val != x; fa = p, p = t[p].ch[x > t[p].val]);
            if (p) ++t[p].cnt;
            else { p = newNode(fa, x); if (fa) t[fa].ch[x > t[fa].val] = p; }
            splay(p, 0);
        }
        void find(int x) {
            if (!root) return; int p = root;
            for (; t[p].val != x && t[p].ch[x > t[p].val]; p = t[p].ch[x > t[p].val]);
            splay(p, 0);
        }
        int next(int x, bool f) { //f=1后继,0前驱
            find(x); int p = root;
            if (f && t[p].val > x) return p;
            if (!f && t[p].val < x) return p;
            for (p = t[p].ch[f]; t[p].ch[f ^ 1]; p = t[p].ch[f ^ 1]);
            return p;
        }
        void detel(int x) {
            int pre = next(x, 0), nex = next(x, 1);
            splay(pre, 0); splay(nex, pre);
            int del = t[nex].ch[0];
            if (t[del].cnt > 1) --t[del].cnt, splay(del, 0);
            else t[nex].ch[0] = 0;
        }
        int kth(int k) {
            if (!root || t[root].siz < k) return -1;
            int p = root;
            while (1) {
                if (t[t[p].ch[0]].siz + t[p].cnt < k)
                    k -= t[t[p].ch[0]].siz + t[p].cnt, p = t[p].ch[1];
                else
                    if (t[t[p].ch[0]].siz >= k) p = t[p].ch[0]; else return t[p].val;
            }
        }
        int getrk(int x){return find(x), t[root].val != x ? -1 : t[t[root].ch[0]].siz + 1;}
    } T;
    int main() {
        IOS; cin >> n;
        T.insert(2e9); T.insert(-2e9);
        rep (i, 1, n) {
            int x, op; cin >> op >> x;
            if (op == 1) T.insert(x);
            else if (op == 2) T.detel(x);
            else if (op == 3) cout << T.getrk(x) - 1 << '
    ';
            else if (op == 4) cout << T.kth(x + 1) << '
    ';
            else if (op == 5) cout << T.t[T.next(x, 0)].val << '
    ';
            else cout << T.t[T.next(x, 1)].val << '
    ';
        }
        return 0;
    }
    

    FHQ 平衡树

    纯板子

    struct FHQ {
        static const int N = 1e5 + 5;
        struct Node {
            int ch[2], val, pri, siz;
            Node (int S = 0, int V = 0, int P = 0, int l = 0, int r = 0) :
                siz(S), val(V), pri(P) { ch[0] = l, ch[1] = r; }
            int& operator [](const int k) { return ch[k]; }
        } tr[N];
        FHQ() { srand((unsigned)time(NULL)); }
        int tot, x, y, z, root;
        int newNode (int v) { tr[++tot] = Node(1, v, rand()); return tot; }
        void update(int x) { tr[x].siz = 1 + tr[tr[x][0]].siz + tr[tr[x][1]].siz; }
        int merge(int x, int y) {
            if (!x || !y) return x + y;
            if (tr[x].pri<tr[y].pri) {tr[x][1] = merge(tr[x][1], y); update(x); return x;}
            else { tr[y][0] = merge(x, tr[y][0]); update(y); return y; }
        }
        void split_v(int p, int k, int &x, int& y) {
            if (!p) x = y = 0;
            else {
                if (tr[p].val <= k) x = p, split_v(tr[p][1], k, tr[p][1], y);
                else y = p, split_v(tr[p][0], k, x, tr[p][0]);
                update(p);
            }
        }
        void split_k(int p, int k, int &x, int &y) {
            if (!p) x = y = 0;
            else {
                if (k <= tr[tr[p][0]].siz) y = p, split_k(tr[p][0], k, x, tr[p][0]);
                else x = p, split_k(tr[p][1], k - tr[tr[p][0]].siz - 1, tr[p][1], y);
                update(p);
            }
        }
        int kth(int p, int k) {
            while (1) {
                if (k <= tr[tr[p][0]].siz) p = tr[p][0];
                else if (k == tr[tr[p][0]].siz + 1) return p;
                else k -= tr[tr[p][0]].siz + 1, p = tr[p][1];
            }
        }
        int getrk(int val) {
            split_v(root, val - 1, x, y);
            int ans = tr[x].siz + 1;
            return root = merge(x, y), ans;
        }
        void add_v(int pos, int val) {
            split_v(root, pos, x, y);
            root = merge(merge(x, newNode(val)), y);
        }
        void del_v(int val) {
            split_v(root, val, x, z);
            split_v(x, val - 1, x, y);
            y = merge(tr[y][0], tr[y][1]);
            root = merge(merge(x, y), z);
        }
        void del_k(int k) {
            split_k(root, k, x, z);
            split_k(x, k - 1, x, y);
            y = merge(tr[y][0], tr[y][1]);
            root = merge(merge(x, y), z);
        }
        int pre(int val) {
            split_v(root, val - 1, x, y);
            int ans = tr[kth(x, tr[x].siz)].val;
            return root = merge(x, y), ans;
        }
        int nxt(int val) {
            split_v(root, val, x, y);
            int ans = tr[kth(y, 1)].val;
            return root = merge(x, y), ans;
        }
    } T;
    

    区间反转/轮换

    struct FHQ {
        static const int N = 5e5 + 5;
        struct Node {
            int ch[2], pri, siz; ll tag, val, miv; bool rever;
            int& operator [](const int k) { return ch[k]; }
        } tr[N];
        FHQ () { srand((unsigned)time(NULL)); }
        int tot, x, y, z, root;
        int newNode (int val) {
            tr[++tot].val = val; tr[tot].pri = rand(); tr[tot].tag = 0;
            tr[tot].rever = 0; tr[tot][0] = tr[tot][1] = 0;
            return tr[tot].miv = val, tr[tot].siz = 1, tot;
        }
        void push_up(int p) {
            tr[p].siz = 1 + tr[tr[p][0]].siz + tr[tr[p][1]].siz; tr[p].miv = tr[p].val;
            if (tr[p][0]) umin(tr[p].miv, tr[tr[p][0]].miv);
            if (tr[p][1]) umin(tr[p].miv, tr[tr[p][1]].miv);
        }
        void push_down(int p) {
            tr[tr[p][0]].tag += tr[p].tag; tr[tr[p][1]].tag += tr[p].tag;
            tr[tr[p][0]].val += tr[p].tag; tr[tr[p][1]].val += tr[p].tag;
            tr[tr[p][0]].miv += tr[p].tag; tr[tr[p][1]].miv += tr[p].tag;
            if (tr[p].rever) 
                tr[tr[p][0]].rever^=1, tr[tr[p][1]].rever^=1, swap(tr[p][0],tr[p][1]);
            tr[p].rever = tr[p].tag = 0;
        }
        int merge(int x, int y) {
            if (!x || !y) return x + y; push_down(x); push_down(y);
            if (tr[x].pri < tr[y].pri) tr[x][1] = merge(tr[x][1], y);
            else tr[y][0] = merge(x, tr[y][0]), swap(x, y);
            push_up(x); return x;
        }
        void split(int p, int k, int &x, int &y) {
            if (!p) x = y = 0;
            else {
                push_down(p);
                if (k <= tr[tr[p][0]].siz) y = p, split(tr[p][0], k, x, tr[p][0]);
                else x = p, split(tr[p][1], k - tr[tr[p][0]].siz - 1, tr[p][1], y);
                push_up(p);
            }
        }
        void add(int pos, int val) {
            split(root, pos, x, y); root = merge(merge(x, newNode(val)), y);
        }
        void del(int k){split(root,k,x,z);split(x,k-1,x,y);root=merge(x, z);}
        void res(int l, int r) { //反转l, r
            split(root, r, x, z); split(x, l - 1, x, y);
            tr[y].rever ^= 1; root = merge(merge(x, y), z);
        }
        void rev(int l, int r, ll t) { //[l,r] t次轮换
            t %= (r - l + 1); if (!t) return;
            split(root,r,root,z);split(root,r-t,root,y);split(root,l-1,root,x);
            root = merge(merge(root, y), merge(x, z));
        }
        void change(int l, int r, ll k) { //[l,r] 区间+k
            split(root, r, x, z); split(x, l - 1, x, y);
            tr[y].tag += k, tr[y].val += k, tr[y].miv += k; root = merge(merge(x, y), z);
        }
        ll minval(int l, int r) { //[l,r]最小值
            split(root, r, x, z); split(x, l - 1, x, y);
            ll ans = tr[y].miv; return root = merge(merge(x, y), z), ans;
        }
    } T;
    

    FHQ启发式合并

    struct FHQ {
        static const int N = 4e5 + 5;
        FHQ() { srand(time(0)); }
        struct Node {
            int ch[2], val, pri; ll siz, cnt;
            int& operator [](int k) { return ch[k]; }
        } tr[N];
        int rt[N], tot;
        void push_up(int p){tr[p].siz = tr[tr[p][0]].siz + tr[tr[p][1]].siz + tr[p].cnt;}
        int newNode(int v, ll c) {
            tr[++tot].val = v; tr[tot].siz = tr[tot].cnt = c;
            return tr[tot][0] = tr[tot][1] = 0; tr[tot].pri = rand(), tot;
        }
        void split(int p, int k, int& x, int& y) {
            if (!p) x = y = 0;
            else {
                if (tr[p].val <= k) x = p, split(tr[p][1], k, tr[p][1], y);
                else y = p, split(tr[p][0], k, x, tr[p][0]);
                push_up(p);
            }
        }
        int merge(int x, int y) {
            if (!x || !y) return x | y;
            if (tr[x].pri < tr[y].pri) tr[x][1] = merge(tr[x][1], y);
            else tr[y][0] = merge(x, tr[y][0]), swap(x, y);
            push_up(x); return x;
        }
        int split(int& p, int l, int r) {
            int x,y,z;split(p,r,x,z);split(x,l-1,x,y);p=merge(x,z);return y;
        }
        int unit(int x, int y) {
            if (!x || !y) return x | y;
            if (tr[x].pri > tr[y].pri) swap(x, y);
            int a, b, c; split(y, tr[x].val, a, b); split(a, tr[x].val - 1, a, c);
            if (c) tr[x].cnt += tr[c].cnt, tr[x].siz += tr[c].siz;
            tr[x][0] = unit(tr[x][0], a); tr[x][1] = unit(tr[x][1], b);
            push_up(x); return x;
        }
        void insert(int& p, int v, ll c) {
            if (!c) return;
            int x, y, z; split(p, v, x, z); split(x, v - 1, x, y);
            if (!y) y = newNode(v, c);
            else tr[y].cnt += c, tr[y].siz += c;
            p = merge(merge(x, y), z);
        }
        ll ask(int& p, int l, int r) {
            int x, y, z; split(p, r, x, z); split(x, l - 1, x, y);
            ll ans = tr[y].siz; p = merge(merge(x, y), z);return ans;
        }
        int kth(int p, ll k) {
            if (k > tr[p].siz) return -1;
            while (1)
                if (tr[tr[p][0]].siz >= k) p = tr[p][0];
                else if (tr[tr[p][0]].siz + tr[p].cnt >= k) return tr[p].val;
                else k -= tr[tr[p][0]].siz + tr[p].cnt, p = tr[p][1];
        }
    } T;
    int n, m, _, k, tot = 1;
    void solve0() { //把p中值域[x,y]分出建新树
        int p, x, y; cin >> p >> x >> y;
        T.rt[++tot] = T.split(T.rt[p], x, y);
    }
    void solve1() { //合并y和x T.rt[x] = T.rt[y]
        int x, y; cin >> x >> y;
        T.rt[x] = T.unit(T.rt[x], T.rt[y]);
    }
    void solve2() { //在树p加入y个x
        int p, x, y; cin >> p >> x >> y;
        T.insert(T.rt[p], y, x);
    }
    ll solve3() { //求树p值域[x,y]数的个数
        int p, x, y; cin >> p >> x >> y;
        return T.ask(T.rt[p], x, y);
    }
    int solve4() { //求树p中第k小
        ll p, k; cin >> p >> k;
        return T.kth(T.rt[p], k);
    }
    

    数学

    素数

    米勒拉宾判素数

    bool millerRabbin(int n) {
        if (n < 3) return n == 2;
        int a = n - 1, b = 0;
        while (a % 2 == 0) a /= 2, ++b;
        srand(time(0));
        //test_time为测试次数,建议设为不小于8,也不应太大
        for (int i = 1, j; i <= test_time; ++i) {
            int x = rand() % (n - 2) + 2, v = qpow(x, a, n);//x^a%n
            if (v == 1 || v == n - 1) continue;
            for (j = 0; j < b; ++j) {
                v = (long long)v * v % n;
                if (v == n - 1) break;
            }
            if (j >= b) return 0;
        }
        return 1;
    }
    

    反素数

    如果某个正整数n满足如下条件,则称为是反素数:任何小于n的正数的约数个数都小于n的约数个数,即因子最多的数(因子数相同取最小的数)

    因子数为n的最小数

    ull p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}, ans;
    int n;
    // depth: 当前在枚举第几个素数。num: 当前因子数。
    // temp: 当前因子数量为num的时候的数值。
    // up:上一个素数的幂,这次应该小于等于这个幂次
    void dfs(int depth, ull temp, int num, int up) {
        if (num > n || depth > 15) return;
        if (num == n && ans > temp) { ans = temp; return; }
        for (int i = 1; i <= up; ++i) {
            if (temp * p[depth] >= ans) break;
            dfs(depth + 1, temp *= p[depth], num * (i + 1), i);
        }
    }
    int main() {
        while (scanf("%d", &n) != EOF) {
            ans = ~0ULL; dfs(0, 1, 1, 64);
            printf("%llu
    ", ans);
        }
        return 0;
    }
    

    小于n因子数最大的数

    ull p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}, ans;
    int ans_num, n; //ans为n以内的最大反素数(会持续更新),ans_sum为ans的因子数。
    void dfs(int depth, ull temp, int num, int up) {
        if (depth > 15 || temp > n) return;
        if (num > ans_num) { ans = temp; ans_num = num; }
        if (num == ans_num && ans > temp) ans = temp;
        for (int i = 1; i <= up; ++i) {
            if (temp * p[depth] > n) break;
            dfs(depth + 1, temp *= p[depth], num * (i + 1), i);
        }
    }
    int main() {
        while (scanf("%d", &n) != EOF) {
            ans_num = 0; dfs(0, 1, 1, 64);
            printf("%llu
    ", ans);
        }
        return 0;
    }
    

    公约数/倍数

    gcd

    gcd(a, b) = gcd(b, a-b) = gcd(a, a-b) = gcd(b, a % b) = gcd(a+b, a)
    gcd(a, b, c, d, .., z) = gcd(a, b - a, c, d, ..., z - a)
    gcd(a[i], a[i + 1], ..., a[j]) = gcd(a[i], a[i + 1] - a[i], ..., a[j] - a[j - 1])
    这样对于数组区间修改相当于在差分数组上单点修改, 可用线段树维护区间gcd, a[i]用树状维护

    lcm

    lcm[a, b] = c * b / gcd(a, b)

    扩展欧几里得

    ll exgcd(ll a, ll b, ll& x, ll& y) {
        if (b == 0) { x = 1, y = 0; return a; }
        ll d = exgcd(b, a % b, y, x); y -= a / b * x;
        return d;
    }
    

    欧拉函数

    求n的欧拉函数

    int getphi(int n) {
        int ans = n;
        rep (i, 2, n / i) if (n % i == 0) {
            ans = ans / i * (i - 1);
            while (n % i == 0) n /= i;
        }
        if (n > 1) ans = ans / n * (n - 1);
        return ans;
    }
    

    筛法

    Euler筛法(pri, phi, mu)

    int v[N], pri[N], phi[N], mu[N], tot;
    void init(int n) {
        memset(v, 0, sizeof v); tot = 0; mu[1] = 1;
        rep (i, 2, n) {
            if (v[i] == 0) mu[v[i] = pri[++tot] = i] = -1, phi[i] = i - 1;
            rep (j, 1, tot) {
                if (pri[j] > v[i] || pri[j] > n / i) break;
                v[i * pri[j]] = pri[j]; //每个数的最小质因子
                phi[i * pri[j]] = phi[i] * (pri[i] - (i % pri[j] != 0));
                mu[i * pri[j]] = i % pri[j] ? -mu[i] : 0;
            }
        }
    }
    

    Euler筛法(约数个数)

    约数个数定理: 若(n=Pi^m_{i=1}p_i^{c_i}), 则(cnt=Pi^m_{i=1}c_i+1)
    (d_i=Pi^m_{i=1}c_i+1) 是积性函数, 自然可以用Euler筛法

    int v[N], pri[N], d[N], num[N], tot; //num最小质数个数
    void init(int n) {
        memset(v, 0, sizeof v); tot = 0; d[1] = 1;
        rep (i, 2, n) {
            if (v[i] == 0) v[i] = pri[++tot] = i, d[i] = 2, num[i] = 1;
            rep (j, 1, tot) {
                if (pri[j] > v[i] || pri[j] > n / i) break;
                int cur = i * pri[j]; v[cur] = pri[j]; //每个数的最小质因子
                num[cur] = i % pri[j] ? 1 : num[i] + 1;
                d[cur] = i % pri[j] ? d[i] * 2 : d[i] / num[cur] * (num[cur] + 1);
            }
        }
    }
    

    Euler筛法(约数和)

    int v[N], pri[N], g[N], f[N], tot;//f[i]i约数和, g[i]i的最小质因子的等比数列和,必要开ll
    void init(int n) {
        memset(v, 0, sizeof v); tot = 0; f[1] = g[1] = 1;
        rep (i, 2, n) {
            if (v[i] == 0) v[i] = pri[++tot] = i, f[i] = g[i] = i + 1;
            rep (j, 1, tot) {
                if (pri[j] > v[i] || pri[j] > n / i) break;
                int cur = i * pri[j]; v[cur] = pri[j]; //每个数的最小质因子
                g[cur] = i % pri[j] ? 1 + pri[j] : g[i] * pri[j] + 1;
                f[cur] = i % pri[j] ? f[i] * f[pri[j]] : f[i] / g[i] * g[cur];
            }
        }
        rep (i, 1, n) f[i] = (f[i - 1] + f[i]) % mod;
    }
    

    欧拉定理 && 费马小定理 && 裴蜀定理

    费马小定理

    若p为素数, (gcd(p, a)=1), 则(a^{p-1}equiv 1(mod p))
    对任意整数 (a^pequiv a(mod p))

    欧拉定理

    (gcd(a,m)=1), 则(a^{varphi (m)}equiv 1 (mod m))

    扩展欧拉定理

    (a^b(mod p) equiv left{egin{matrix} a^{b mod varphi (p)}, & gcd(a,p)=1\ a^b, & gcd(a,p) eq 1,bleqslant varphi (p)\ a^{b mod varphi (p) + varphi (p)}, & gcd(a,p) eq 1,b geqslant varphi(p) end{matrix} ight.)

    裴蜀定理

    任意x,y不全为零, 存在(ax+by=gcd(a,b))

    乘法逆元

    单个逆元

    快速幂(a^{-1}(mod p) equiv a^{p-2}(mod p))

    线性逆元

    inv[0] = inv[1] = 1;
    rep (i, 2, n) inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
    

    线性求任意 n 个数的逆元

    s[0] = 1;
    rep (i, 1, n) s[i] = s[i - 1] * a[i] % p;
    sv[n] = qpow(s[n], p - 2);
    per (i, n, 1) sv[i - 1] = sv[i] * a[i] % p;
    rep (i, 1, n) inv[i] = sv[i] * s[i - 1] % p;
    

    线性同余方程

    (axequiv c(mod b))等价于(ax+by=c)
    (gcd(a,b)|c)时有解, (ax_0+by_0=gcd(x,y))
    通解为(x=frac{c}{gcd(a,b)}x_0+kfrac{b}{gcd(a,b)}, y=frac{c}{gcd(a,b)}y_0-kfrac{a}{gcd(a,b)})

    中国剩余定理

    模数两两互质

    ll CRT(ll a[], ll m[], int n) {
        ll t = 1, ans = 0;
        for (int i = 0; i < n; ++i) t *= m[i];
        for (int i = 0; i < n; ++i) {
            ll cur = t / m[i], x, y; exgcd(cur, m[i], x, y);
            ans = (ans + a[i] * cur % t * x % t) % t;
        }
        return (ans + t) % t;
    }
    

    模数不互质

    (left{egin{matrix} x equiv a_1 (mod m_1)\ x equiv a_2 (mod m_2)\ ...\ x equiv a_k (mod m_k)end{matrix} ight.)
    (x=m_1p+a_1=m_2q+a_2 p,q∈Z), 则(m_1p-m_2q=a_2-a_1)
    由裴蜀定理得(a_2-a_1=0(mod gcd(m_1,m_2)))否则无解
    则合并两项得出通解(x equiv m_1p+a_1(mod lcm(m_1,m_2))), 不断合并求出x

    ll mod(ll x, ll p) { return (x % p + p) % p; }
    ll CRT(ll a[], ll m[], int n) {
        for (int i = 2; i <= n; ++i) {
            ll k1, k2, d = exgcd(m[1], m[i], k1, k2);
            ll c = mod(a[i] - a[1], m[i]);
            if (c % d) return -1;
            ll p = m[i] / d; c = c / d % p; k1 = mod(k1 * c, p);
            a[1] += m[1] * k1; m[1] = m[1] / d * m[i];
        }
        return a[1];
    }
    

    baby-step giant-step

    (a^xequiv b(mod p), aperp p)

    ll baby_step_giant_step(ll a, ll b, ll p) {
        unordered_map<ll, ll> st; b %= p;
        int t = sqrt(p - 1) + 1; ll cur = 1;
        for (int i = 0; i < t; ++i, cur = cur * a % p) st[b * cur % p] = i;
        a = qpow(a, t, p); cur = a;
        if (a == 0) return b == 0 ? 1 : -1;
        for (int i = 1; i <= t; ++i, cur = cur * a % p) {
            ll c = st.count(cur) ? st[cur] : -1;
            if (c >= 0 && i * t - c >= 0) return i * t - c;
        }    
        return -1;
    }
    

    (a^xequiv b(mod p))

    具体地,设(d_1=gcd(a,p))。如果(d_1 mid b),则原方程无解。否则我们把方程同时除以(b_1),得到

    (frac{a}{d_1}a^{x-1}equiv frac{b}{d_1}(mod frac{p}{d_1}))

    如果(a)(p)仍不互质就再除,设(d_2=gcd(a,p))。如果(d_2 mid frac{b}{d_1}),则方程无解;否则同时除以(d_2)得到

    (frac{a^2}{d_1d_2}a^{x-2}equiv frac{b}{d_1d_2}(mod frac{p}{d_1d_2}))

    同理,这样不停的判断下去。直到(aperp frac{p}{d_1d_2...d_k})

    (D=prod_{i=1}^k d_i),于是方程就变成了这样:(frac{a^k}{D}a^{x-k}equiv frac{b}{D}(mod frac{p}{D}))

    由于(aperpfrac{p}{D}),于是(frac{a^k}{D}perpfrac{p}{D})推出。这样(frac{a^k}{D})就有逆元了,于是把它丢到方程右边,这就是一个普通的(BSGS)问题了,于是求解(x-k)后再加上(k)就是原方程的解啦。

    注意,不排除解小于等于(k)的情况,所以在消因子之前做一下(O(k))枚举,直接验证(a^iequiv b(mod p))

    高斯消元

    无模数(用double)

    double a[N][N];
    int gauss(int n, int m) {
        int c = 1, r = 1;
        for (int t = r; c < m && r <= n; ++c, t = r) {
            rep (i, r + 1, n) if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
            if (fabs(a[t][c]) < eps) continue;
            if (t != r) rep (i, 1, m) swap(a[t][i], a[r][i]);
            rep (i, c + 1, m) a[r][i] /= a[r][c]; a[r][c] = 1;
            for (int i = r + 1; i <= n; a[i++][c] = 0) if (a[i][c])
                rep (j, c + 1, m) a[i][j] -= a[i][c] * a[r][j];
            ++r;
        }
        rep (i, r, n) if (a[i][m]) return -1;
        if (r < m) return 0;
        per (i, m - 2, 1) rep (j, i + 1, m - 1) a[i][m] -= a[j][m] * a[i][j];
        return 1;
    }
    

    有模数(用费马定理)

    int a[N][N];
    int gauss(int n, int m) {
        int c = 1, r = 1;
        for (int t = -1; c < m && r <= n; ++c, t = -1) {
            rep (i, r, n) if (a[i][c]) { t = i; break; }
            if (t == -1) continue;
            if (t != r) swap(a[t], a[r]);
            a[r][c] = qpow(a[r][c], mod - 2);
            rep (i, c + 1, m) a[r][i] = (ll)a[r][i] * a[r][c] % mod; a[r][c] = 1;
            for (int i = r + 1; i <= n; a[i++][c] = 0) if (a[i][c])
                rep (j, c + 1, m) a[i][j]=((a[i][j] - a[r][j] * a[i][c])%mod+mod)%mod;
            ++r;
        }
        rep (i, r, n) if (a[i][m]) return -1;
        if (r < m) return 0;
        per (i, m - 1, 1) rep (j, i + 1, m - 1)
            a[i][m] = ((a[i][m] - a[j][m] * a[i][j]) % mod + mod) % mod;
        return 1;
    }
    

    异或消元

    bitset<N> d[N];
    ll gauss(int n, int m) {
        int c = 1, r = 1;
        for (int t = r; c <= m && r <= n; ++c, t = ++r) {
            rep(i, r + 1, n) if (d[i][c]) t = i;
            if (!d[t][c]) { --r; continue; }
            if (t != r) swap(d[t], d[r]);
            for (int i = r + 1; i <= n; d[i++][c] = 0) if (d[i][c])
                d[i] ^= d[r];
        }
        return n - r + 1;
    }
    

    线性基

    struct XXJ {
        static const int N = 59;
        ll g[N + 1]; bool zero = 0; vector<ll> a;
        void init() { memset(g, 0, sizeof g); zero = 0; }
        bool insert(ll x) {
            per (i, N, 0) if (x >> i & 1)
                if (!g[i]) {g[i] = x; return 1; } else x ^= g[i];
            return zero = 1, 0;
        }
        bool isin(ll x) { per (i, N, 0) if (x >> i & 1) x ^= g[i]; return !x; }
        ll max() { ll mx = 0; per (i, N, 0) umax(mx, mx ^ g[i]); return mx; }
        ll min() { if (zero) return 0; rep (i, 0, N) if (g[i]) return g[i]; }
        void build() {
            vector<ll>().swap(a);
            rep (i, 0, N) {
                per (j, i - 1, 0) if (g[i] >> j & 1) g[i] ^= g[j];
                if (g[i]) a.emplace_back(g[i]);
            }
        }
        ll kth(ll k) {
            ll ans = 0; k -= zero; if (k <= 0) return !k ? 0 : -1;
            if (k >> a.size()) return -1;
            rep (i, 0, a.size() - 1) if (k >> i & 1) ans ^= a[i];
            return ans;
        }
    } xxj;
    

    排列组合

    多重集的组合数1

    (S={n_1*a_1,n_2*a_2,...,n_k*a_k})选r个元素((r leqslant min(n_1))), 组合数为(S=C_{r+(k-1)}^{r-1})

    多重集的组合数2

    (S={n_1*a_1,n_2*a_2,...,n_k*a_k})选r个元素, 组合数为(|igcap_i^k sum S_i|=S-|igcup_i^k sum ar{S_i}|=sum_iC_{r+k-1-(n_i+1)}^{k-1} - sum_{i,j}C_{r+k-1-(n_i+1)-(n_j+1)}^{k-1}+..+(-1)^kC_{r-1-sum_{i=k}^kn_i}^{k-1})

    不相邻的排列

    从1~n个数选k个数互不相邻, (S=C_{n-k+1}^k)

    错排公式

    (f(n)=(n-1)(f(n-1)+f(n-2))=left lfloor n!/e+0.5 ight floor)

    园排列

    (Q_n^r=frac{A_n^r}{r}=frac{n!}{r*(n-r)!})

    Catlan

    n个0, n个1, 任意前缀0个数大于1的个数, (Cat_n = frac{C^n_{2n}}{n+1})
    n对括号匹配, n个数进出栈, n个节点构成二叉树, (0,0)->(n,m)除端点不接触y=x走法(2Cat_{n-1})

    Lucas定理

    (C_n^m=C_{n mod p}^{m mod p} * Lucas_{n/p}^{m/p} mod p)

    ll C(int n, int m, int k) {
        return m>n?0:fac[k][n]*facinv[k][m]%mod[k]*facinv[k][n - m]%mod[k];
    }
    ll lucas(ll n, ll m, int k) {
        return m?C(n%mod[k],m%mod[k],k)*lucas(n/mod[k],m/mod[k],k)%mod[k]:1;
    }
    

    莫比乌斯

    miu[1] = 1;
    rep (i, 2, n) {
        if (!v[i]) prime[++tot] = i, miu[i] = -1;
        for (int j = 1; prime[j] <= n / i && j <= tot; ++j) {
            v[prime[j] * i] = 1;
            if(i % prime[j] == 0) break;
            else miu[i * prime[j]] = -miu[i];
        }
    }
    

    进制任意转换

    void work(int a, int b, string& s) {
        int len = int(s.size());
        VI cur(len), ans;
        rep(i, 0, len - 1) cur[i] = s[i] - (s[i]<='9'?'0':s[i]<='Z'?'A'-9:'a'-35);
        for (int i = 0; i < len;) {
            rep (j, i + 1, len - 1) cur[j] += cur[j - 1] % b * a, cur[j - 1] /= b;
            ans.pb(cur[len - 1] % m); cur[len - 1] /= m;
            while (i < len && cur[i] == 0) ++i;
        } s = "";
        per(i,ans.size()-1,0)s+=char(ans[i]+(ans[i]<=9?'0':ans[i]<=35?'A'-9:'a'-35);
    }
    

    模拟退火

    const double eps = 1e-3;       //精度
    const double start_T = 1000;   //初始温度
    const double rate = 0.98;      //温度下降速率
    int n, m, _, k;
    struct point {
        double x;
        double y;
        double z;
    } p[N];
    double dist(point a, point b) {
        return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z));
    }
    double solve() {
        double T = start_T;
        point ans_p = {0,0,0};  //初始点
        double ans = 1e99;      //预设一个较大值
        while (T > eps) {
            point maxd_p = p[1];
            rep (i, 1, n)
                if (dist(ans_p, p[i]) > dist(ans_p, maxd_p)) maxd_p = p[i];
            //找到距离ans_p最远的点,maxd_p
            ans = min(ans, dist(ans_p, maxd_p));
            ans_p.x += (maxd_p.x - ans_p.x) * (T / start_T);    //以一定概率靠近maxd_p
            ans_p.y += (maxd_p.y - ans_p.y) * (T / start_T);
            ans_p.z += (maxd_p.z - ans_p.z) * (T / start_T);
            T *= rate;
        }
        return ans;
    }
    int main() {
        IOS; cin >> n;
        rep(i, 1, n) cin >> p[i].x >> p[i].y >> p[i].z;
        cout << setiosflags(ios::fixed) << setprecision(8) << solve();
        return 0;
    }
    

    __builtin函数 (__int128适用)

    __builtin_popcount(x); // x二进制1的个数
    __builtin_ffs(x); //比lowbit慢
    __bultin_ctz(x); //x二进制末尾0的个数
    __bultin_clz(x); //二进制前导0, 0未知
    __builtin_parity(x);//x的二进制数中1的个数的奇偶性
    

    大数

    #define MAXN 9999 // MAXN 是一位中最大的数字
    #define MAXSIZE 10024 // MAXSIZE 是位数
    #define DLEN 4 // DLEN 记录压几位
    struct Big {
        int a[MAXSIZE], len;
        bool flag;  // 标记符号'-'
        Big() { len = 1; memset(a, 0, sizeof a); flag = 0; }
        Big(const int);
        Big(const char*);
        Big(const Big&);
        Big& operator=(const Big&);
        Big operator+(const Big&) const;
        Big operator-(const Big&) const;
        Big operator*(const Big&)const;
        Big operator/(const int&) const;
        Big operator^(const int&) const;
        // TODO: Big 位运算;
        int operator%(const int&) const;
        // TODO: Big ^ Big;
        bool operator<(const Big&) const;
        bool operator<(const int& t) const;
        inline void print() const;
    };
    Big::Big(const int b) {
        int c, d = b; len = 0;
        CLR(a); //memset(a,0,sizeof a);
        while (d > MAXN) {
            c = d - (d / (MAXN + 1) * (MAXN + 1));
            d = d / (MAXN + 1); a[len++] = c;
        }
        a[len++] = d;
    }
    Big::Big(const char* s) {
        int t, k, index, l; CLR(a);
        l = strlen(s); len = l / DLEN;
        if (l % DLEN) ++len;
        index = 0;
        for (int i = l - 1; i >= 0; i -= DLEN) {
            t = 0; k = i - DLEN + 1;
            if (k < 0) k = 0;
            g(j, k, i) t = t * 10 + s[j] - '0';
            a[index++] = t;
        }
    }
    Big::Big(const Big& T) : len(T.len) {
        CLR(a);
        for (int i = 0; i < len; ++i) a[i] = T.a[i];
        // TODO:重载此处?
    }
    Big& Big::operator=(const Big& T) {
        CLR(a); len = T.len;
        for (int i = 0; i < len; ++i) a[i] = T.a[i];
        return *this;
    }
    Big Big::operator+(const Big& T) const {
        Big t(*this);
        int big = len;
        if (T.len > len) big = T.len;
        for (int i = 0; i < big; ++i) {
            t.a[i] += T.a[i];
            if (t.a[i] > MAXN) { ++t.a[i + 1]; t.a[i] -= MAXN + 1; }
        }
        if (t.a[big]) t.len = big + 1;
        else t.len = big;
        return t;
    }
    Big Big::operator-(const Big& T) const {
        int big;
        bool ctf;
        Big t1, t2;
        if (*this < T) {
            t1 = T; t2 = *this; ctf = 1;
        }
        else { t1 = *this; t2 = T; ctf = 0; }
        big = t1.len;
        int j = 0;
        for (int i = 0; i < big; ++i) {
            if (t1.a[i] < t2.a[i]) {
                j = i + 1;
                while (t1.a[j] == 0) ++j;
                --t1.a[j--];
                // WTF?
                while (j > i) t1.a[j--] += MAXN;
                t1.a[i] += MAXN + 1 - t2.a[i];
            }
            else t1.a[i] -= t2.a[i];
        }
        t1.len = big;
        while (t1.len > 1 && t1.a[t1.len - 1] == 0) { --t1.len; --big; }
        if (ctf) t1.a[big - 1] = -t1.a[big - 1];
        return t1;
    }
    Big Big::operator*(const Big& T) const {
        Big res;
        int up, te, tee;
        for (int i = 0; i < len; ++i) {
            up = 0;
            for (int j = 0; j < T.len; ++j) {
                te = a[i] * T.a[j] + res.a[i + j] + up;
                if (te > MAXN) {
                    tee = te - te / (MAXN + 1) * (MAXN + 1);
                    up = te / (MAXN + 1);
                    res.a[i + j] = tee;
                }
                else { up = 0; res.a[i + j] = te; }
            }
            if (up) res.a[i + T.len] = up;
        }
        res.len = len + T.len;
        while (res.len > 1 && res.a[res.len - 1] == 0) --res.len;
        return res;
    }
    Big Big::operator/(const int& b) const {
        Big res;
        int down = 0;
        gd(i, len - 1, 0) {
            res.a[i] = (a[i] + down * (MAXN + 1) / b);
            down = a[i] + down * (MAXN + 1) - res.a[i] * b;
        }
        res.len = len;
        while (res.len > 1 && res.a[res.len - 1] == 0) --res.len;
        return res;
    }
    int Big::operator%(const int& b) const {
        int d = 0;
        gd(i, len - 1, 0) d = (d * (MAXN + 1) % b + a[i]) % b;
        return d;
    }
    Big Big::operator^(const int& n) const {
        Big t(n), res(1);
        int y = n;
        while (y) {
            if (y & 1) res = res * t;
            t = t * t;
            y >>= 1;
        }
        return res;
    }
    bool Big::operator<(const Big& T) const {
        int ln;
        if (len < T.len) return 233;
        if (len == T.len) {
            ln = len - 1;
            while (ln >= 0 && a[ln] == T.a[ln]) --ln;
            if (ln >= 0 && a[ln] < T.a[ln]) return 233;
            return 0;
        }
        return 0;
    }
    inline bool Big::operator<(const int& t) const {
        Big tee(t);
        return *this < tee;
    }
    inline void Big::print() const {
        printf("%d", a[len - 1]);
        gd(i, len - 2, 0) { printf("%04d", a[i]); }
    }
    inline void print(Big s) { // s不要是引用,要不然你怎么print(a * b);
        int len = s.len;
        printf("%d", s.a[len - 1]);
        gd(i, len - 2, 0) { printf("%04d", s.a[i]); }
    }
    char s[100024];
    

    计算几何二维

    const int N = 262144 + 3;
    
    /*一:【准备工作】*/
    #define int register int
    const double eps = 1e-8, Pi = acos(-1.0);
    inline int dcmp(double a) { return a < -eps ? -1 : (a > eps ? 1 : 0); }//处理精度
    inline double Abs(double a) { return a * dcmp(a); }//取绝对值
    struct Point {
        double x, y; Point(double X = 0, double Y = 0) { x = X, y = Y; }
        inline void in() { scanf("%lf%lf", &x, &y); }
        inline void out() { printf("%.2lf %.2lf
    ", x, y); }
    };
    
    /*二:【向量】*/
    inline double Dot(Point a, Point b) { return a.x * b.x + a.y * b.y; }//【点积】
    inline double Cro(Point a, Point b) { return a.x * b.y - a.y * b.x; }//【叉积】
    inline double Len(Point a) { return sqrt(Dot(a, a)); }//【模长】
    inline double Angle(Point a, Point b) { return acos(Dot(a, b) / Len(a) / Len(b)); }//【两向量夹角】
    inline Point Normal(Point a) { return Point(-a.y, a.x); }//【法向量】
    inline Point operator+(Point a, Point b) { return Point(a.x + b.x, a.y + b.y); }
    inline Point operator-(Point a, Point b) { return Point(a.x - b.x, a.y - b.y); }
    inline Point operator*(Point a, double b) { return Point(a.x * b, a.y * b); }
    inline bool operator==(Point a, Point b) { return !dcmp(a.x - b.x) && !dcmp(a.y - b.y); }
    //两点坐标重合则相等
    
    /*三:【点、向量的位置变换】*/
    /*1.【点、向量的旋转】*/
    inline Point turn_P(Point a, double theta) {
        //【点A向量A顺时针旋转theta(弧度)】
        double x = a.x * cos(theta) + a.y * sin(theta);
        double y = -a.x * sin(theta) + a.y * cos(theta);
        return Point(x, y);
    }
    inline Point turn_PP(Point a, Point b, double theta) {
        //【将点A绕点B顺时针旋转theta(弧度)】
        double x = (a.x - b.x) * cos(theta) + (a.y - b.y) * sin(theta) + b.x;
        double y = -(a.x - b.x) * sin(theta) + (a.y - b.y) * cos(theta) + b.y;
        return Point(x, y);
    }
    
    
    /*四:【图形与图形之间的关系】*/
    
    /*1.【点与线段】*/
    inline int pan_PL(Point p, Point a, Point b) {//【判断点P是否在线段AB上】
        return !dcmp(Cro(p - a, b - a)) && dcmp(Dot(p - a, p - b)) <= 0;//做法一
    //  return !dcmp(Cro(p-a,b-a))&&dcmp(min(a.x,b.x)-p.x)<=0&&
    //  dcmp(p.x-max(a.x,b.x))<=0&&dcmp(min(a.y,b.y)-p.y)<=0&&
    //  dcmp(p.y-max(a.y,b.y))<=0;//做法二
        //PA,AB共线且P在AB之间(其实也可以用len(p-a)+len(p-b)==len(a-b)判断,但是精度损失较大)
    }
    inline double dis_PL(Point p, Point a, Point b) {//【点P到线段AB距离】
        if (a == b)return Len(p - a);//AB重合
        Point x = p - a, y = p - b, z = b - a;
        if (dcmp(Dot(x, z)) < 0)return Len(x);//P距离A更近
        if (dcmp(Dot(y, z)) > 0)return Len(y);//P距离B更近
        return Abs(Cro(x, z) / Len(z));//面积除以底边长
    }
    
    /*2.【点与直线】*/
    inline int pan_PL_(Point p, Point a, Point b) {
        //【判断点P是否在直线AB上】
        return !dcmp(Cro(p - a, b - a));//PA,AB共线
    }
    inline Point FootPoint(Point p, Point a, Point b) {
        /
            /【点P到直线AB的垂足】
            Point x = p - a, y = p - b, z = b - a;
        double len1 = Dot(x, z) / Len(z), len2 = -1.0 * Dot(y, z) / Len(z);
        //分别计算AP,BP在AB,BA上的投影
        return a + z * (len1 / (len1 + len2));//点A加上向量AF
    }
    inline Point Symmetry_PL(Point p, Point a, Point b) {
        //【点P关于直线AB的对称点】
        return p + (FootPoint(p, a, b) - p) * 2;//将PF延长一倍即可
    }
    
    /*3.【线与线】*/
    inline Point cross_LL(Point a, Point b, Point c, Point d) {
        //【两直线AB,CD的交点】
        Point x = b - a, y = d - c, z = a - c;
        return a + x * (Cro(y, z) / Cro(x, y));//点A加上向量AF
    }
    inline int pan_cross_L_L(Point a, Point b, Point c, Point d) {
        //【判断直线AB与线段CD是否相交】
        return pan_PL(cross_LL(a, b, c, d), c, d);
        //直线AB与直线CD的交点在线段CD上
    }
    inline int pan_cross_LL(Point a, Point b, Point c, Point d) {
        //【判断两线段AB,CD是否相交】
        double c1 = Cro(b - a, c - a), c2 = Cro(b - a, d - a);
        double d1 = Cro(d - c, a - c), d2 = Cro(d - c, b - c);
        return dcmp(c1) * dcmp(c2) < 0 && dcmp(d1) * dcmp(d2) < 0;//分别在两侧
    }
    
    /*4.【点与多边形】*/
    inline int PIP(Point* P, int n, Point a) {
        //【射线法】判断点A是否在任意多边形Poly以内
        int cnt = 0; double tmp;
        for (int i = 1; i <= n; ++i) {
            int j = i < n ? i + 1 : 1;
            if (pan_PL(a, P[i], P[j]))return 2;//点在多边形上
            if (a.y >= min(P[i].y, P[j].y) && a.y < max(P[i].y, P[j].y))
                //纵坐标在该线段两端点之间
                tmp = P[i].x + (a.y - P[i].y) / (P[j].y - P[i].y)
                * (P[j].x - P[i].x), cnt += dcmp(tmp - a.x) > 0;//交点在A右方
        }
        return cnt & 1;//穿过奇数次则在多边形以内
    }
    inline int judge(Point a, Point L, Point R) {//判断AL是否在AR右边
        return dcmp(Cro(L - a, R - a)) > 0;//必须严格以内
    }
    inline int PIP_(Point* P, int n, Point a) {
        //【二分法】判断点A是否在凸多边形Poly以内
        //点按逆时针给出
        if (judge(P[1], a, P[2]) || judge(P[1], P[n], a))return 0;
        //在P[1_2]或P[1_n]外
        if (pan_PL(a, P[1], P[2]) || pan_PL(a, P[1], P[n]))return 2;
        //在P[1_2]或P[1_n]上
        int l = 2, r = n - 1;
        while (l < r) {
            //二分找到一个位置pos使得P[1]_A在P[1_pos],P[1_(pos+1)]之间
            int mid = l + r + 1 >> 1;
            if (judge(P[1], P[mid], a))l = mid;
            else r = mid - 1;
        }
        if (judge(P[l], a, P[l + 1]))return 0;//在P[pos_(pos+1)]外
        if (pan_PL(a, P[l], P[l + 1]))return 2;//在P[pos_(pos+1)]上
        return 1;
    }
    
    /*5.【线与多边形】*/
    
    /*6.【多边形与多边形】*/
    inline int judge_PP(Point* A, int n, Point* B, int m) {
        //【判断多边形A与多边形B是否相离】
        for (int i1 = 1; i1 <= n; ++i1) {
            int j1 = i1 < n ? i1 + 1 : 1;
            for (int i2 = 1; i2 <= m; ++i2) {
                int j2 = i2 < m ? i2 + 1 : 1;
                if (pan_cross_LL(A[i1], A[j1], B[i2], B[j2]))return 0;
                //两线段相交
                if (PIP(B, m, A[i1]) || PIP(A, n, B[i2]))return 0;//点包含在内
            }
        }
        return 1;
    }
    
    /*五:【图形面积】*/
    
    /*1.【任意多边形面积】*/
    inline double PolyArea(Point* P, int n) {//任意多边形P的面积,逆时针给出点序,或者先求一遍凸包,把点排序
        double S = 0;
        for (int i = 1; i <= n; ++i)S += Cro(P[i], P[i < n ? i + 1 : 1]);
        return S / 2.0;
    }
    
    /*2.【圆的面积并】*/
    
    /*3.【三角形面积并】*/
    
    /*六:【凸包】*/
    
    /*1.【求凸包】*/
    inline bool cmp1(Point a, Point b) { return a.x == b.x ? a.y < b.y : a.x < b.x; };
    //按坐标排序
    inline int ConvexHull(Point* P, int n, Point* cp) {
        //【Graham扫描法】求凸包
        sort(P + 1, P + n + 1, cmp1);
        int t = 0;
        for (int i = 1; i <= n; ++i) {//下凸包
            while (t > 1 && dcmp(Cro(cp[t] - cp[t - 1], P[i] - cp[t - 1])) <= 0)--t;
            cp[++t] = P[i];
        }
        int St = t;
        for (int i = n - 1; i >= 1; --i) {//上凸包
            while (t > St && dcmp(Cro(cp[t] - cp[t - 1], P[i] - cp[t - 1])) <= 0)--t;
            cp[++t] = P[i];
        }
        return --t;//要减一
    }
    /*2.【旋转卡壳】*/
    
    /*3.【半平面交】*/
    struct Line {
        Point a, b; double k; Line(Point A = Point(0, 0), Point B = Point(0, 0))
        {
            a = A, b = B, k = atan2(b.y - a.y, b.x - a.x);
        }
        inline bool operator<(const Line& O)const
        {
            return dcmp(k - O.k) ? dcmp(k - O.k) < 0 : judge(O.a, O.b, a);
        }
        //如果角度相等则取左边的
    }L[N], Q[N];
    inline Point cross(Line L1, Line L2) { return cross_LL(L1.a, L1.b, L2.a, L2.b); }
    //获取直线L1,L2的交点
    inline int judge(Line L, Point a) { return dcmp(Cro(a - L.a, L.b - L.a)) > 0; }
    //判断点a是否在直线L的右边
    inline int halfcut(Line* L, int n, Point* P) {//【半平面交】,逆时针,求凸包排点序,再求线Line(p[i - 1], p[i])
        sort(L + 1, L + n + 1); int m = n; n = 0;
        for (int i = 1; i <= m; ++i)if (i == 1 || dcmp(L[i].k - L[i - 1].k))L[++n] = L[i];
        int h = 1, t = 0;
        for (int i = 1; i <= n; ++i) {
            while (h < t && judge(L[i], cross(Q[t], Q[t - 1])))--t; //当队尾两个直线交点不是在直线L[i]上或者左边时就出队
            while (h < t && judge(L[i], cross(Q[h], Q[h + 1])))++h; //当队头两个直线交点不是在直线L[i]上或者左边时就出队
            Q[++t] = L[i];
        }
        while (h < t && judge(Q[h], cross(Q[t], Q[t - 1])))--t;
        while (h < t && judge(Q[t], cross(Q[h], Q[h + 1])))++h;
        n = 0;
        for (int i = h; i <= t; ++i)P[++n] = cross(Q[i], Q[i < t ? i + 1 : h]);
        return n;
    }
    
    /*4.【闵可夫斯基和】*/
    Point V1[N], V2[N];
    inline int Mincowski(Point* P1, int n, Point* P2, int m, Point* V) {
        //【闵可夫斯基和】求两个凸包{P1},{P2}的向量集合{V}={P1+P2}构成的凸包
        for (int i = 1; i <= n; ++i)V1[i] = P1[i < n ? i + 1 : 1] - P1[i];
        for (int i = 1; i <= m; ++i)V2[i] = P2[i < m ? i + 1 : 1] - P2[i];
        int t = 0, i = 1, j = 1; V[++t] = P1[1] + P2[1];
        while (i <= n && j <= m)++t, V[t] = V[t - 1] + (dcmp(Cro(V1[i], V2[j])) > 0 ? V1[i++] : V2[j++]);
        while (i <= n)++t, V[t] = V[t - 1] + V1[i++];
        while (j <= m)++t, V[t] = V[t - 1] + V2[j++];
        return t;
    }
    
    /*5.【动态凸包】*/
    
    /*七:【圆】*/
    
    /*1.【三点确定一圆】*/
    #define S(a) ((a)*(a))
    struct Circle { Point O; double r; Circle(Point P, double R = 0) { O = P, r = R; } };
    inline Circle getCircle(Point A, Point B, Point C) {
        //【三点确定一圆】暴力解方程
        double x1 = A.x, y1 = A.y, x2 = B.x, y2 = B.y, x3 = C.x, y3 = C.y;
        double D = ((S(x2) + S(y2) - S(x3) - S(y3)) * (y1 - y2) - (S(x1) + S(y1) -
            S(x2) - S(y2)) * (y2 - y3)) / ((x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2));
        double E = (S(x1) + S(y1) - S(x2) - S(y2) + D * (x1 - x2)) / (y2 - y1);
        double F = -(S(x1) + S(y1) + D * x1 + E * y1);
        return Circle(Point(-D / 2.0, -E / 2.0), sqrt((S(D) + S(E) - 4.0 * F) / 4.0));
    }
    inline Circle getcircle(Point A, Point B, Point C) {
        //【三点确定一圆】向量垂心法
        Point P1 = (A + B) * 0.5, P2 = (A + C) * 0.5;
        Point O = cross_LL(P1, P1 + Normal(B - A), P2, P2 + Normal(C - A));
        return Circle(O, Len(A - O));
    }
    
    /*2.【最小覆盖圆】*/
    inline int PIC(Circle C, Point a) { return dcmp(Len(a - C.O) - C.r) <= 0; }
    //判断点A是否在圆C内
    inline void Random(Point* P, int n)
    {
        for (int i = 1; i <= n; ++i)swap(P[i], P[rand() % n + 1]);
    }//随机一个排列
    inline Circle Min_Circle(Point* P, int n) {//【求点集P的最小覆盖圆】
    //  random_shuffle(P+1,P+n+1);
        Random(P, n); Circle C = Circle(P[1], 0);
        for (int i = 2; i <= n; ++i)if (!PIC(C, P[i])) {
            C = Circle(P[i], 0);
            for (int j = 1; j < i; ++j)if (!PIC(C, P[j])) {
                C.O = (P[i] + P[j]) * 0.5, C.r = Len(P[j] - C.O);
                for (int k = 1; k < j; ++k)if (!PIC(C, P[k]))C = getcircle(P[i], P[j], P[k]);
            }
        }
        return C;
    }
    
    /*3.【三角剖分】*/
    inline double calc(Point A, Point B, Point O, double R) {//【三角剖分】
        if (A == O || B == O)return 0;
        int op = dcmp(Cro(A - O, B - O)) > 0 ? 1 : -1; double ans = 0;
        Point x = A - O, y = B - O;
        int flag1 = dcmp(Len(x) - R) > 0, flag2 = dcmp(Len(y) - R) > 0;
        if (!flag1 && !flag2)ans = Abs(Cro(A - O, B - O)) / 2.0;//两个点都在里面
        else if (flag1 && flag2) {//两个点都在外面
            if (dcmp(dis_PL(O, A, B) - R) >= 0)ans = R * R * Angle(x, y) / 2.0;//完全包含了圆弧
            else {//分三段处理 △+圆弧+△
                if (dcmp(Cro(A - O, B - O)) > 0)swap(A, B);//把A换到左边
                Point F = FootPoint(O, A, B); double lenx = Len(F - O), len = sqrt(R * R - lenx * lenx);
                Point z = turn_P(F - O, Pi / 2.0) * (len / lenx); Point B_ = F + z, A_ = F - z;
                ans = R * R * (Angle(A - O, A_ - O) + Angle(B - O, B_ - O)) / 2.0 + Cro(B_ - O, A_ - O) / 2.0;
            }
        }
        else {//一个点在里面,一个点在外面
            if (flag1)swap(A, B);//使A为里面的点,B为外面的点
            Point F = FootPoint(O, A, B); double lenx = Len(F - O), len = sqrt(R * R - lenx * lenx);
            Point z = turn_P(F - O, Pi / 2.0) * (len / lenx); Point C = dcmp(Cro(A - O, B - O)) > 0 ? F - z : F + z;
            ans = Abs(Cro(A - O, C - O)) / 2.0 + R * R * Angle(C - O, B - O) / 2.0;
        }
        return ans * op;
    }
    

    计算机和三维球的交

    img

    (S=2pi∗R∗H)

    (V=pi*H^2*frac{3*R-H}{3})

    #define sqr(n) ((n) * (n))
    const double PI = acos(-1.0);
    struct point { double x, y, z; };
    struct circle { point o; double r; };
    double getlen(point a, point b) {
        return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z));
    }
    double getCroArea(circle a, circle b) {
        if (a.r > b.r) swap(a, b);
        double dis = getlen(a.o, b.o);
        if (dis + a.r <= b.r) {
            double r = max(a.r, b.r);
            return 4 * PI * r * r;
        } else if (dis < a.r + b.r && dis + a.r > b.r) {
            double angle_cosa = (a.r * a.r + dis * dis - b.r * b.r) / (2 * a.r * dis);
            double angle_cosb = (b.r * b.r + dis * dis - a.r * a.r) / (2 * b.r * dis);
            double len_a = a.r - a.r * angle_cosa, len_b = b.r - b.r * angle_cosb;
            double ans = 4 * PI * (a.r * a.r + b.r * b.r);
            ans -= 2 * PI * (a.r * len_a + b.r * len_b);
            return ans;
        }
        else return 4 * PI * (a.r * a.r + b.r * b.r);
        return 0;
    }
    double getCroVol(circle o, circle t) {
        if (o.r < t.r) swap(o, t);
        double dis = sqrt(sqr(o.o.x - t.o.x) + sqr(o.o.y - t.o.y) + sqr(o.o.z - t.o.z)), ans = 0;
        if (dis <= o.r - t.r)
            ans = 4.0 / 3 * PI * t.r * t.r * t.r;
        else if (dis < o.r + t.r) {
            double cal = (o.r * o.r + dis * dis -t.r * t.r) / (dis * o.r * 2);
            double h = o.r * (1 - cal);
            ans = PI / 3 * (3 * o.r - h) * h * h;
            cal = (t.r * t.r + dis * dis - o.r * o.r) / (dis * t.r * 2);
            h = t.r * (1 - cal);
            ans += PI / 3 * (3 * t.r - h) * h * h;
        }
        return ans;
    }
    

    乱七八糟的公式

    莫比乌斯

    b(abc) 为 abc的因子个数
    (f(a,b,c) = sum_i^asum_j^bsum_k^cb(a*b*c) ≡ g(a,b,c) = sum_{gcd(i,j,k)=1} left lfloor frac{a}{i} ight floor left lfloor frac{b}{j} ight floor left lfloor frac{c}{k} ight floor)
    (f(a,b)=sum_i^asum_j^bb(a*b) ≡ g(a,b) = sum_{gcd(i,j)=1}left lfloor frac{a}{i} ight floor left lfloor frac{b}{j} ight floor)

    图论

    最短路

    迪杰斯特拉

    无法判负边,负环

    贝尔曼

    可以判负环, 用spfa, 最坏 O(NM), 可以判负环进行差分约束

    bfs判负环

    bool check(double mid) {
        stack<int> st; rep(i, 1, n) dis[i] = dep[i] = 0, v[i] = 1, st.push(i);
        while (!st.empty()) {
            int x = st.top(); st.pop(); v[x] = 0;
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
                if (dis[y] <= dis[x] + co[i]) continue;
                dis[y] = dis[x] + co[i]; dep[y] = dep[x] + 1;
                if (dep[y] >= n) return 1; /*有负环*/ if (!v[y]) st.push(y), v[y] = 1;
            }
        } return 0;
    }
    

    dfs 判负环

    bool dfs(int x) {
        v[x] = 1;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
            if (dis[y] > dis[x] + co[i]) {
                dis[y] = dis[x] + co[i];
                if (v[y] || dfs(y)) return 1; //有负环
            }
        return v[x] = 0;
    }
    

    弗洛伊德

    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n) umin(d[i][j], d[i][k] + d[k][j]);
    

    最小生成树

    kruskal

    prim

    次小严格生成树

    找到非生成边(x, y)去替换树上x到y路径的最大边,费用差值最小,就是次小和最小生成树的差值

    const int N = 1e5 + 5, M = 3e5 + 5;
    struct STFrom {
        int f[N][20], dep[N], lg[N], t;//N为节点的数量
        ll d[N][20], b[N][20];
        vector<PII> *h;
        void init(int n, vector<PII> *H) {
            t = log2(n - 1) + 1; h = H; lg[0] = -1;
            rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
        }
        void bfs(int s) {
            queue<int> q; q.push(s); dep[s] = 1;
            rep(i, 0, t) f[s][i] = 0, d[s][i] = b[s][i] = 0;
            while (!q.empty()) {
                int x = q.front(); q.pop();
                for (auto &y : h[x]) {
                    if (dep[y.fi]) continue;
                    dep[y.fi] = dep[x] + 1; f[y.fi][0] = x; q.push(y.fi);
                    d[y.fi][0] = y.se; b[y.fi][0] = -2e18;
                    for (int j = 1; j <= t; ++j) {
                        f[y.fi][j] = f[f[y.fi][j - 1]][j - 1];
                        if (d[f[y.fi][j - 1]][j - 1] > d[y.fi][j - 1])
                            b[y.fi][j] = max(d[y.fi][j - 1], b[f[y.fi][j - 1]][j - 1]),
                            d[y.fi][j] = d[f[y.fi][j - 1]][j - 1];
                        else if (d[f[y.fi][j - 1]][j - 1] == d[y.fi][j - 1])
                            b[y.fi][j] = max(b[y.fi][j - 1], b[f[y.fi][j - 1]][j - 1]),
                            d[y.fi][j] = d[y.fi][j - 1];
                        else b[y.fi][j] = max(b[y.fi][j - 1], d[f[y.fi][j - 1]][j - 1]),
                            d[y.fi][j] = d[y.fi][j - 1];
                    }
                }
            }
        }
        void work(PLL& ans, int y, int i) {
            if (d[y][i] > ans.fi) ans.se = max(ans.fi, b[y][i]), ans.fi = d[y][i];
            else if (d[y][i] == ans.fi) umax(ans.se, b[y][i]);
            else umax(ans.se, d[y][i]);
        }
        PLL ask(int x, int y) {
            PLL ans = {-2e18, -2e18};
            if (dep[x] > dep[y]) swap(x, y);
            for(int k = dep[y] - dep[x], i = lg[k]; ~i; --i) if (k >> i & 1)
                work(ans, y, i), y = f[y][i];k
            if (x == y) return ans;
            per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) {
                work(ans, x, i); work(ans, y, i);
                x = f[x][i], y = f[y][i];
            }
            work(ans, x, 0); work(ans, y, 0);
            return ans;
        }
    } ST;
    struct edge {int x, y; ll c; bool f = 0; } e[M];
    int n, m, _, k, cas, f[N];
    ll res, ans = 2e18;
    vector<PII> h[N];
    int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
    int main() {
        IOS; cin >> n >> m;
        rep (i, 1, m) cin >> e[i].x >> e[i].y >> e[i].c;
        sort(e + 1 , e + 1 + m, [](edge& a, edge& b) { return a.c < b.c; });
        rep (i, 1, n) f[i] = i;
        rep (i, 1, m) {
            int x = find(e[i].x), y = find(e[i].y);
            if (x == y) continue;
            res += e[i].c, e[i].f = 1; f[y] = x;
            h[e[i].x].pb(e[i].y, e[i].c); h[e[i].y].pb(e[i].x, e[i].c);
        }
        ST.init(n, h); ST.bfs(1);
        rep (i, 1, m) if (!e[i].f) {
            auto cur = ST.ask(e[i].x, e[i].y);
            if (cur.fi < e[i].c) umin(ans, res - cur.fi + e[i].c);
            else if(cur.se != -2e18 && cur.se < e[i].c) umin(ans, res - cur.se + e[i].c); 
        }
        return cout << ans, 0;
    }
    

    最小树形图朱刘算法O(NM)

    struct Edge { int x, y, w; } e[N];
    ll zhuLiu (int root, int n, vector<Edge>& e) { //从0开始,下标从1开始传n + 1
        ll ans = 0; VI in(n), pre(n); //in为最小入边权, pre为其对应的起点
        while (true) {
            for (auto &i : in) i = INF;
            //可以添加虚点,连向每个点w=inf,ans>=2*inf无解,在添加最小边的时
            //选了虚边,另一端是实际源点,但点的编号已经改变,只能记录用了哪条边,最后还原点
            /* if (e[i].w < in[e[i].y] && e[i].x != e[i].y) {
                    pre[e[i].y] = e[i].x, in[e[i].y] = e[i].w;*/
                    /*if (e[i].x == root) pos = i;//记录连接实际root的虚边
                } */
            for (auto &i:e) if (i.w < in[i.y] && i.x != i.y) pre[i.y] = i.x, in[i.y] = i.w;
            rep (i, 0, n - 1) if (i != root && in[i] == INF) return -1;
            int cnt = in[root] = 0; VI idx(n, -1), v(n, -1);
            rep (i, 0, n - 1) {//标记每个环
                int y = i; ans += in[i];//记录权值
                while (v[y] != i && idx[y] == -1 && y != root) v[y] = i, y = pre[y];
                if (y != root && idx[y] == -1) {
                    for(int x = pre[y]; x != y; x = pre[x]) idx[x] = cnt;
                    idx[y] = cnt++;
                }
            } if (cnt == 0) break;
            for (auto &i : idx) if (i == -1) i = cnt++;
            for (auto &i : e) {
                if (idx[i.x] != idx[i.y]) i.w -= in[i.y];
                i.x = idx[i.x], i.y = idx[i.y];
            } n = cnt; root = idx[root];
        } return ans;
    }
    

    树的重心 (最多两个且相连)

    int mx; VI gra;
    void dfs(int x, int fa) {
        siz[x] = 1; int max_size = 0;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
            if (y == fa) continue;
            dfs(y); siz[x] += siz[y]; umax(max_size, siz[y]);
        }
        umax(max_size, n - siz[x]);
        if (mx > max_size) mx = max_size, gra.resize(0), gra.pb(x);
        else if (mx == max_size) gra.pb(x);
    }
    

    差分约束

    记得在加完题目约束之后,别忘了i和i+1的默认约束,且别忘了超级源点让图联通

    树的直径(可以有很多条)

    两次dfs求出最远的两个点 (保留路径方便, 边权为非负)

    void dfs(int u, int fa) {
        for (int i = h[u], y = to[i]; i; y = to[i = ne[i]]) {
            if (fa != y) continue;
            f[y] = u; b[y] = i; d[y] = d[u] + co[i];
            if (d[0] < d[y]) d[0] = d[y], f[0] = y;
            dfs(y, x);
        }
    }
    void work(int& p, int& q) {
        d[0] = -N; d[1] = 0; dfs(1, 0); p = f[0];
        d[0] = -N; d[p] = 0; dfs(p, 0); q = f[0];
    }
    

    dp(边权任意, 保留路径困难)

    void dpfind(int u, int fa, int& ans) {
        for (int i = h[u]; i; i = ne[i]) {
            int y = to[i];
            if (fa != y) continue;
            dpfind(y, u, ans);
            umax(ans, d[u] + d[y] + co[i]), umax(d[u], d[y] + co[i]);
        } umax(d[u], 0);
    }
    

    最近公共祖先

    struct STFrom {
        int f[N][20], dep[N], lg[N], t;//N为节点的数量
        int *h, *ne, *to;
        void init(int n, int* H, int* Ne, int* To) {
            t = log2(n - 1) + 1; h = H, ne = Ne, to = To; lg[0] = -1;
            rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
        }
        void bfs(int s) {
            queue<int> q; q.push(s); dep[s] = 1;
            rep(i, 0, t) f[s][i] = 0;
            while (!q.empty()) {
                int x = q.front(); q.pop();
                for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
                    if (dep[y]) continue;
                    dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                    for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
                }
            }
        }
        int lca(int x, int y) {
            if (dep[x] > dep[y]) swap(x, y);
            for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
            if (x == y) return x;
            per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
            return f[x][0];
        }
        int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)]<<1); }
    } ST;
    

    基环树

    所有问题都分为, 在同一颗子树和环上不同子树来处理,
    且处理子树, 莫走到其他子树上, 直接从环上节点作为子树根, 然后再也不走到环上

    无向基环树

    pair<int, ll> cir[N]; //<环上节点编号, 从第个点到此点的距离>
    bool v[N]; //是否在环上
    int dfsc(int u, int bian) {
        idx[u] = idcnt; int s = 0;
        for (int i = h[u]; i; i = ne[i]) {
            int y = to[i];
            if (bian == (i ^ 1) || v[y]) continue;
            if (!idx[y]) s = max(s, dfsc(y, i));
            else cir[idc[s = u] = ++cid] = u, v[u] = 1;
        }
        return s;
    }
    void work(int u) {
        cid = 0; ++idcnt;
        for (u = a[dfsc(u, 0)], ls = 0;u != cir[1]; ls = idc[u], u = a[u]) 
            cir[idc[u] = ++cid] = { u, cir[ls].se + 1 }, v[u] = 1;
        sizc[idcnt] = cid;
    }
    

    无向基环树森林直径和

    rep (i, 1, n) if (!dfn[i]) ans += work(i)
    ll work(int u) {
        ll ans = cid = 0; tarjan(u, 0);//找出当前基环树的环
        rep (i, 1, cid) {
            cir[i + cid] = cir[i]; cir[i + cid].se += cir[cid].se + cir[0].se;
            dpfind(cir[i].fi, 0, ans); //dp找环上节点子树的直径
        }
        int tail = -1, head = 0;; cid <<= 1;
        q[++tail] = { 1, dis[cir[1].fi] };
        rep(i, 2, cid) {
            while (i - q[head].fi >= cid >> 1) ++head;
            ans = max(ans, dis[cir[i].fi] + cir[i].se + q[head].se);
            ll w = dis[cir[i].fi] - cir[i].se;
            while (head <= tail && q[tail].se <= w) --tail;
            q[++tail] = { i, w };
        } return ans;
    }
    

    内向树森林

    要反向建边, 才能从环上子树走向环

    树链剖分

    基础

    struct BIT {
        static const int N = 1e5 + 5;
        struct node { int l, r, len; ll val, tag; } tr[N << 2];
        void push_up(int rt) { tr[rt].val = (tr[rt << 1 | 1].val + tr[rt << 1].val) % mod; }
        void push_down(int rt) {
            if (!tr[rt].tag) return;
            tr[rt << 1].val = (tr[rt << 1].val + tr[rt << 1].len * tr[rt].tag % mod) % mod;
            tr[rt<<1|1].val=(tr[rt<<1|1].val+tr[rt<<1|1].len*tr[rt].tag%mod)%mod;
            tr[rt << 1].tag = (tr[rt << 1].tag + tr[rt].tag) % mod;
            tr[rt << 1 | 1].tag = (tr[rt << 1 | 1].tag + tr[rt].tag) % mod; tr[rt].tag = 0;
        }
        void build(int rt, int l, int r, int* a) {
            tr[rt] = { l, r, r - l + 1, 0 };
            if (l == r) { tr[rt].val = a[l] % mod; return; }
            int mid = l + r >> 1;
            build(rt << 1, l, mid, a); build(rt << 1 | 1, mid + 1, r, a); push_up(rt);
        }
        void change(int rt, int l, int r, ll k) {
            if (tr[rt].l >= l && tr[rt].r <= r) {
                tr[rt].val = (tr[rt].val + tr[rt].len * k % mod) % mod;
                tr[rt].tag = (k + tr[rt].tag) % mod; return;
            }
            push_down(rt);
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (mid >= l) change(rt << 1, l, r, k);
            if (mid < r) change(rt << 1 | 1, l, r, k);
            push_up(rt);
        }
        ll ask(int rt, int l, int r) {
            if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt].val;
            push_down(rt);
            int mid = tr[rt].l + tr[rt].r >> 1;
            ll ans = mid >= l ? ask(rt << 1, l, r) : 0;
            if (mid < r) ans = (ans + ask(rt << 1 | 1, l, r)) % mod;
            push_up(rt); return ans;
        }
    } bit;
    VI h[N];
    int a[N], na[N], dep[N], hson[N], siz[N], fa[N], top[N], dfn[N], df;
    void findhson(int x, int f) {
        dep[x] = dep[f] + 1; siz[x] = 1; fa[x] = f;
        int mx = 0;
        for (auto y : h[x]) {
            if (y == f) continue; findhson(y, x);
            if (mx < siz[y]) mx = siz[y], hson[x] = y; siz[x] += siz[y];
        }
    }
    void dfs(int x, int f) {
        na[dfn[x] = ++df] = a[x]; top[x] = f;
        if (!hson[x]) return;
        dfs(hson[x], f);
        for (auto y : h[x]) if (y != fa[x] && y != hson[x]) dfs(y, y);
    }
    void solve1() { //将树从 x 到 y 结点最短路径上所有节点的值都加上 z
        int x, y, z; cin >> x >> y >> z; z %= mod;
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) swap(x, y);
            bit.change(1, dfn[top[x]], dfn[x], z); x = fa[top[x]];
        }
        if (dep[x] > dep[y]) swap(x, y);
        bit.change(1, dfn[x], dfn[y], z);
    }
    ll solve2() { //求树从 x 到 y 结点最短路径上所有节点的值之和
        int x, y; cin >> x >> y; ll ans = 0;
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) swap(x, y);
            ans = (ans + bit.ask(1, dfn[top[x]], dfn[x])) % mod; x = fa[top[x]];
        }
        if (dep[x] > dep[y]) swap(x, y);
        return ((ans + bit.ask(1, dfn[x], dfn[y])) % mod + mod) % mod;
    }
    void solve3() { //将以 x 为根节点的子树内所有节点值都加上 z
        int x, z; cin >> x >> z; z %= mod;
        bit.change(1, dfn[x], dfn[x] + siz[x] - 1, z);
    }
    ll solve4() { //求以 x 为根节点的子树内所有节点值之和
        int x; cin >> x;
        return (bit.ask(1, dfn[x], dfn[x] + siz[x] - 1) + mod) % mod;
    }
    int main() {
        IOS; cin >> n >> m >> s >> mod;
        rep(i, 1, n) cin >> a[i];
        rep(i, 2, n) { int u, v; cin >> u >> v; h[u].pb(v); h[v].pb(u); }
        findhson(s, 0); dfs(s, s); bit.build(1, 1, df, na);
        rep(i, 1, m) {
            int op; cin >> op;
            switch (op) {
                case 1: solve1(); break;
                case 2: cout << solve2() << '
    '; break;
                case 3: solve3(); break;
                case 4: cout << solve4() << '
    '; break;
            }
        } return 0;
    }
    

    点分树

    struct STFrom { ... } ST; //LCA的ST表
    struct DTTree {
        static const int N = 1e5 + 5;
        struct node { int fa; } t[N];
        int mxsz[N], csz[N], rt, siz[N];
        int* h, * ne, * to;
        bool v[N];
        void init(int n, int* H, int* Ne, int* To) {
            h = H, ne = Ne, to = To; rt = 0;
            rep(i, 1, n) v[i] = t[i].fa = 0;
        }
        void dfscenter(int x, int f, int sum) {
            csz[x] = 1; mxsz[x] = 0;
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (y != f && !v[y]) {
                dfscenter(y, x, sum); csz[x] += csz[y]; mxsz[x] = max(mxsz[x], csz[y]);
            } umax(mxsz[x], sum - csz[x]);
            if (!rt || mxsz[x] < mxsz[rt]) rt = x;
        }
        void dfs(int x, int sum) {
            v[x] = 1; siz[x] = sum; rt = 0;
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]], rt = 0) if (!v[y]) {
                int yz = csz[y] < csz[x] ? csz[y] : sum - csz[x];
                dfscenter(y, x, yz); t[rt].fa = x; dfs(rt, yz);
            }
        }
    } DT;
    struct DyBIT {
        static const int n = 2e5 + 5;
        struct Node { int l, r, val, lson, rson;} tr[N * 20];
        int root[N], tot;
        void init(int n) { rep(i, 1, n) root[i] = 0; tot = 0; }
        int newNode(){++tot; tr[tot].lson = tr[tot].rson = tr[tot].val = 0; return tot;}
        void change(int& rt, int l, int r, int p, int k) {
            if (!rt) rt = newNode(), tr[rt].l = l, tr[rt].r = r;
            tr[rt].val += k; if (l == r) return;
            int mid = l + r >> 1;
            if (p <= mid) change(tr[rt].lson, l, mid, p, k);
            else change(tr[rt].rson, mid + 1, r, p, k);
        }
        int ask(int rt, int l, int r, int L, int R) {
            if (!rt || L > R) return 0;
            if (l >= L && r <= R) return tr[rt].val;
            int mid = l + r >> 1;
            int ans = L <= mid ? ask(tr[rt].lson, l, mid, L, R) : 0;
            if (R > mid) ans += ask(tr[rt].rson, mid + 1, r, L, R);
            return ans;
        }
    } bita, bitb;
    int w[N];
    void change(int x, int k) {
        for (int p = x, fa; p; p = fa) {
            fa = DT.t[p].fa; bita.change(bita.root[p], 0, DT.siz[p], ST.dist(p, x), k);
            if (fa) bitb.change(bitb.root[p], 0, DT.siz[fa], ST.dist(fa, x), k);
        }
    }
    int ask(int x, int k) {
        int ans = 0;
        for(int u=x,p=0,d=k-ST.dist(u,x);u;p=u,d=k-ST.dist(u=DT.t[u].fa,x))if(d >= 0){
            ans += bita.ask(bita.root[u], 0, DT.siz[u], 0, d);
            if (p) ans -= bitb.ask(bitb.root[p], 0, DT.siz[u], 0, d);
        } return ans;
    }
    int main() {
        IOS; while (cin >> n >> m) {
            rep(i, 1, n) cin >> w[i], h[i] = 0; tot = 0;
            rep(i, 2, n) {int u, v; cin >> u >> v; add(u, v); add(v, u);}
            bita.init(n); bitb.init(n);
            ST.init(n, h, ne, to); ST.bfs(1);
            DT.init(n, h, ne, to); DT.dfscenter(1, 0, n); DT.dfs(DT.rt, n);
            rep(i, 1, n) change(i, w[i]); //int las = 0;
            rep(i, 1, m) {
                string op; int x, y; cin >> op >> x >> y;//x ^= las, y ^= las;
                if (op[0] == '?') cout << ask(x, y) << '
    ';
                else change(x, y - w[x]), w[x] = y;
            }
        } return 0;
    }
    

    无向图连通性

    e-dcc缩点, 求桥

    int c[N], ecnt;
    vector<vector<int>> ecc;
    bool edge[M << 1];
    void tarjan(int x, int bian) {
        dfn[st[++top] = x] = low[x] = ++df;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
            if (!dfn[y]) {
                tarjan(y, i ^ 1); umin(low[x], low[y]);
                if (dfn[x] < low[y]) edge[i] = edge[i ^ 1] = 1;
            }
            else if (i ^ bian) low[x] = min(low[x], dfn[y]);
        if (low[x] == dfn[x]) {
            ++ecnt; ecc.pb(VI()); int y;
            do { c[y = st[top--]] = ecnt; ecc.back().pb(y); } while (y != x);
        }
    }
    rep (i, 1, n) if (!dfn[i]) top = 0, tarjan(i, 0);//遍历
    rep (i, 2, tot) { //可能有重边, 新建缩点图
        int x = to[i ^ 1], y = to[i];
        if (c[x] == c[y]) continue;
        add_c(c[x], c[y]);
    }
    

    v-dcc, 求割点

    int newid[N], pre[N], num, c[N], bl[M]; //bl[i], 原先的边属于哪一个vcc
    vector<vector<int>> dcc;
    bool cut[N];
    void tarjan(int x) {
        dfn[x] = low[x] = ++df;
        if (!h[x]) { dcc.pb(vector<int>(1, x)); return; }
        st[++top] = x; int cnt = 0;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
            if (!dfn[y]) {
                tarjan(y); low[x] = min(low[x], low[y]);
                if (dfn[x] <= low[y]) {
                    ++cnt; int z;
                    dcc.pb(vector<int>());
                    if (x != root || cnt > 1) cut[x] = 1;
                    do dcc.back().pb(z = st[top--]); while (z != y);
                    dcc.back().pb(x);
                }
            }
            else umin(low[x], dfn[y]);
    }
    rep (i, 1, n) if (!dfn[i]) top = 0, root = i, tarjan(i);//遍历
    num = dcc.size();
    rep (i, 1, n) if (cut[i]) newid[i] = ++num, pre[num] = i; //切点新编号
    per (i, dcc.size(), 1)
        for (int &j : dcc[i - 1]) {
            c[j] = i;
            if (cut[j]) add_c(newid[j], i), add_c(i, newid[j]);
            for (int k = h[j]; k; k = ne[k]) if (c[to[k]] == i) bl[k >> 1] = i;
        }
    

    欧拉回路

    bool eulerc(int s) {
        for (auto& i : edge)
            if (i.size() & 1) return 0; //存在入度为奇数, 无欧拉回路
            else sort(all(i), greater<PII>()); //把边按照边的权值排序(使得欧拉回路输出字典序最小)
        top = t = 0; st[++top] = 0; st[++top] = s;
        while (top) {
            int x = st[top];
            while (!edge[x].empty() && vis[edge[x].back().fi]) edge[x].pop_back();
            if (!edge[x].empty()) {
                st[++top] = edge[x].back().fi;
                st[++top] = edge[x].back().se;
                vis[st[top - 1]] = 1;
                //标记边(编号)使用过, 另一次访问次边时边的另一节点,
                //如果边可以来回走一次, 那就把加入反边编号
                edge[x].pop_back();
            }
            else ans[++t] = st[(--top)--];
        }
        return 1;
    }
    

    仙人掌(连通图,一条边最多在一个环上) 圆方树

    圆方树,圆点是原来的点,方点代表一个环(拆环),环上的点向这个环对应的方点连边

    void solve(int x, int y, int c) { //再次点双中, 以x为起点, 以x->y方向遍历次点双
        //sum[i]表示从x->y方向到i,i距离x的距离,sum[vcnt+n]是这个点双的环的大小
        sum[++vcnt + n] = sum[y] = c;
        for (int i = y; i != x; i = fa[i]) sum[fa[i]] = sum[vcnt + n] += dist[i];
        sum[x] = 0;
        for (int i = y; i != fa[x]; i = fa[i]) { //点双上园点向方点连边
            int c = min(sum[i], sum[vcnt + n] - sum[i]);
            add_c(i, vcnt + n, c); add_c(vcnt + n, i, c);
        }
    }
    void tarjan(int x) {
        dfn[x] = low[x] = ++df;
        if (!h[x]) return; st[++top] = x;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
            if (!dfn[y]) {
                dist[y] = co[i]; fa[y] = x;
                tarjan(y); umin(low[x], low[y]);
                if (dfn[x] <= low[y]) { //找到一个点双
                    int z = st[top], c;
                    while (y != st[top--]);
                    for(int j = h[z]; j; j = ne[j]) if(to[j] == x) { c = co[j]; break; }
                    solve(x, z, c);
                }
            }
            else umin(low[x], dfn[y]);
    }
    

    有向图的连通性

    scc缩点

    int c[N], scnt;
    vector<VI> scc;
    bool inst[N]; //是否在栈中
    void tarjan(int x) {
        dfn[x] = low[x] = ++df; inst[st[++top] = x] = 1;
        for (int i = h[x], y = to[i] ; i; y = to[i = ne[i]])
            if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if (inst[y]) low[x] = min(low[x], dfn[y]);
        if (low[x] == dfn[x]) {
            ++scnt; scc.pb(VI());
            for (int y = 0; y ^ x; y = st[top--])
                inst[st[top]] = 0, c[st[top]] = scnt, scc.back().pb(st[top]);
        }
    }
    rep (i, 1, n)
        for (int k = h[i], y; k; k = ne[k]) {
            if (c[i] == c[y = to[k]]) continue;
            add_c(c[i], c[y]);
        }
    

    2-SAT问题

    int n, m, _, k, h[N][N];
    int dfn[N], low[N], df, st[N], top;
    int c[N], scnt, opp[N], val[N];
    bool inst[N];
    pair<int, int> t[N];
    
    void tarjan(int x) {
        dfn[x] = low[x] = ++df, inst[st[++top] = x] = 1;
        for (int y = 1; y <= m; ++y) if (h[x][y])
            if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if (inst[y]) low[x] = min(low[x], dfn[y]);
        if (low[x] == dfn[x]) {
            ++scnt; int y;
            do inst[y = st[top--]] = 0, c[y] = scnt; while (y != x);
        }
    }
    
    void _print(int c) {
        int h = c / 60, m = c % 60;
        if (h < 10) printf("0%d:", h);
        else printf("%d:", h);
        if (m < 10) printf("0%d", m);
        else printf("%d", m);
    }
    
    void print(int x) { _print(t[x].first); printf(" "); _print(t[x].second); puts(""); }
    
    int main() {
        scanf("%d", &n); bool f = 1; m = n << 1;
        for (int i = 1; i <= n; ++i) {
            int a, b, c, d, e; opp[i] = i + n, opp[i + n] = i;
            scanf("%d:%d %d:%d %d", &a, &b, &c, &d, &e);
            t[i] = { a * 60 + b, a * 60 + b + e }; t[i + n] = { c * 60 + d - e, c * 60 + d };
            if (t[i].first > t[i].second || t[i + n].first > t[i + n].second) f = 0;
        }
        for (int i = 1; i < m; ++i) for (int j = i + 1; j <= m; ++j)
            if (i == j || j == i + n) continue;
            else if (t[i].second > t[j].first && t[i].first < t[j].second) h[i][opp[j]] = h[j][opp[i]] = 1;
        for (int i = 1; i <= m; ++i) if (!dfn[i]) top = 0, tarjan(i);
        for (int i = 1; i <= n; ++i) if (c[i] == c[i + n]) return puts("NO"), 0;
        puts("YES");
        for (int i = 1; i <= m; ++i) val[i] = c[i] > c[opp[i]];
        for (int i = 1; i <= n; ++i) print(val[i] ? opp[i] : i);
        return 0;
    }
    第 i 对情侣需要 Di 分钟完成这个仪式,即必须选择 Si∼Si+Di 或 Ti−Di∼Ti 两个时间段之一。
    
    牧师想知道他能否满足每场婚礼的要求,即给每对情侣安排Si∼Si+Di 或 Ti−Di∼Ti,使得这些仪式的时间段不重叠。
    
    rep (i, 1, n) opp[i] = n + i, opp[n + i] = i;
    rep (i, 1, n << 1) if (!dfn[i]) top = 0, tarjan(i);
    rep (i, 1, n) if (c[i] == c[i + n]) { puts("-1"); break; }
    rep (i, 1, n << 1) val[i] = c[i] > c[opp[i]];
    // val[i] == 0, 选择 i, val[i] == 1, 选择 i + n
    

    SCC求割点割边

    Lengauer-Tarjan(支配树)

    VI ha[N], hb[N], hc[N];//正边,反边,被半支配点
    int idx[N], dfn[N], df, fa[N], anc[N], best[N];
    int idom[N], sdom[N];
    void dfs(int x) {
        idx[dfn[x] = ++df] = x;
        for (auto &y : ha[x]) if (!dfn[y]) dfs(y), fa[y] = x;
    }
    int find(int x) {
        if (x == anc[x]) return x;
        int y = find(anc[x]);
        if (dfn[sdom[best[anc[x]]]] < dfn[sdom[best[x]]]) best[x] = best[anc[x]];
        return anc[x] = y;
    }
    int eval(int x) { find(x); return best[x]; }
    void tarjan() {
        per (y, df, 2) {
            int x = idx[y];
            for (auto &z : hb[x]) {
                if (!dfn[z]) continue; find(z);
                if (dfn[sdom[best[z]]] < dfn[sdom[x]]) sdom[x] = sdom[best[z]];
            }
            hc[sdom[x]].pb(x);
            x = anc[x] = fa[x];
            for (auto &z : hc[x]) {
                int u = eval(z);
                idom[z] = sdom[u] == x ? x : u;//最近支配点
            }
        }
        rep (i, 2, df) {
            int x = idx[i];
            if (idom[x] != sdom[x]) idom[x] = idom[idom[x]];
        }
    }
    rep (i, 1, n) sdom[i] = anc[i] = best[i] = i;
    dfs(0); tarjan();
    

    给定s,t的必经边、点

    拓扑排序求,fs[x]起点s到x的路径数,ft[x]x到终点t的路径数,双三hash存储(爆ll)
    fs[x]ft[y]=fs[t],(x,y)必经边;fs[x]ft[x]=fs[t],x是必经点

    二分图的匹配

    最大匹配=最小点覆盖=n-最大独立集大小,最大团等于补图的最大独立集
    有向无环图最小路径点覆盖, (路径可覆盖则跑一遍传递闭包), 将每个点拆成入点和出点, n - 拆点二分图最大匹配

    二分图的判定

    染色法

    最大匹配(NN+NM)

    rep (i, 1, n) { rep (j, 1, n) v[j] = 0; m += dfs(i); }
    bool dfs(int x) {
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
            if (v[y]) continue; v[y] = 1;
            if (!match[y] || dfs(match[y])) return match[y] = x, 1;
        } return 0;
    }
    

    KM带权(最大)匹配((N^3))

    int w[N][N], la[N], lb[N], match[N], delta; //边权,左、右顶表
    bool va[N], vb[N]; //左右点是否在交错树中
    bool dfs(int x) {
        va[x] = 1;
        rep (y, 1, n) if (!vb[y])
            if (la[x] + lb[y] == w[x][y]) {
                vb[y] = 1;
                if (!match[y] || dfs(match[y])) { match[y] = x; return 1; }
            }
        return 0;
    }
    int KM() {
        rep (i, 1, n) {
            la[i] = -inf; lb[i] = 0;
            rep (j, 1, n) umax(la[i], w[i][j]);
        }
        rep (i, 1, n)
            while (1) {
                rep (i, 1, n) va[i] = vb[i] = 0;/*;*/delta = inf; if (dfs(i)) break;
                rep (x, 1, n) if (va[x]) rep (y, 1, n) if (!vb[y]) delta = min(delta, la[x] + lb[y] - w[x][y]); 
                rep (j, 1, n) la[j] -= va[j] ? delta : 0, lb[j] += vb[j] ? delta : 0;
            }
        ll ans = 0; rep (i, 1, n) ans += w[match[i]][i]; return ans;
    }
    

    一般图(带花树)O(n^2m)

    int que[M], ql, qr, pre[N], tim = 0;
    int match[N], f[N], tp[N], tic[N];
    int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
    int lca(int x,int y) {
    	for (++tim; ; swap(x,y)) if (x) {
    		x = find(x);
    		if (tic[x] == tim) return x;
            else tic[x] = tim, x = pre[match[x]];
    	}
    }
    void shrink(int x, int y, int p) {
    	while (find(x) != p) {
    		pre[x] = y, y = match[x];
    		if (tp[y] == 2) tp[y] = 1, que[++qr] = y;
    		if (find(x) == x) f[x] = p;
    		if (find(y) == y) f[y] = p;
    		x = pre[y];
    	}
    }
    bool aug(int s) {
    	rep (i, 0, n) f[i] = i, tp[i] = pre[i] = 0;
    	tp[que[ql = qr = 1] = s] = 1; // 1: type A ; 2: type B
    	for (int t = 0; ql <= qr; ) {
    		int x = que[ql++];
    		for (int i = h[x], v = to[i]; i; i = ne[i], v = to[i])
    			if (find(v) == find(x) || tp[v] == 2) continue; 
    			else if (!tp[v]) {
    				tp[v] = 2, pre[v] = x;
    				if (!match[v]) {
    					for (int now = v, last, tmp; now; now = last) {
    						last = match[tmp = pre[now]];
    						match[now] = tmp, match[tmp] = now;
    					} return true;
    				} 
    				tp[match[v]] = 1, que[++qr] = match[v];
    			} else if (tp[v] == 1) {
    				int l = lca(x,v); shrink(x,v,l), shrink(v,x,l);
    			}
        } return false;
    }
    int main() {
    	read(n); int x, y;
    	while (~scanf("%d%d", &x, &y)) add(x,y), add(y,x);
    	int ans = 0; rep (i, 1, n) ans += (!match[i] && aug(i));
    	write(ans << 1); puts("");//ans表示有几对
    	rep (i, 1, n) if (match[i] > i) write(i), putchar(' '), write(match[i]), puts("");
    	return 0;
    }
    

    网络流

    最大流

    Edmonds-Karp (O(nm^2))

    const int N = 2010, M = 20010, inf = 1 << 30;
    int h[N], to[M], ne[M], co[M], tot;
    int v[N], incf[N], pre[N], s, t, maxflow;
    void add(int u, int v, int c) {
        ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
        ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = 0;
    }
    while (cin >> n >> m) {
        rep (i, 1, n) h[i] = 0;
        s = 1, t = n; tot = 1; maxflow = 0;
        rep (i, 1, m) {
            int u, v, c; cin >> u >> v >> c;
            add(u, v, c);
        }
        while (bfs()) update();
        cout << maxflow << '
    ';
    }
    bool bfs () {
        rep (i, 1, n) v[i] = 0;
        queue<int> q; q.push(s); v[s] = 1;
        incf[s] = inf; //增广路上各边的最小剩余容量
        while (!q.empty()) {
            int x = q.front(); q.pop(); v[x] = 0;
            for (int i = h[x]; i; i = ne[i]) {
                if (!co[i]) continue;
                int y = to[i];
                if (v[y]) continue;
                incf[y] = min(incf[x], co[i]);
                pre[y] = i; q.push(y); v[y] = 1;
                if (y == t) return 1;
            }
        }
        return 0;
    }
    void update() {
        int x = t;
        while (x != s) {
            int i = pre[x];
            co[i] -= incf[t]; co[i ^ 1] += incf[t];
            x = to[i ^ 1];
        }
        maxflow += incf[t];
    }
    

    Dinic (O(n^2m))

    const int N = 5e4 + 5, M = 3e5 + 5, inf = 1 << 30;
    int d[N], s, t, maxflow;
    tot = 1;
    int flow = 0;
    while (bfs()) while (flow = dinic(s, inf)) maxflow += flow;
    bool bfs() {
        memset(d, 0, sizeof d); memcpy(now, h, sizeof h);
        queue<int> q; q.push(s); d[s] = 1;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) if (co[i] && !d[y]) {
                d[y] = d[x] + 1; q.push(y);
                if (y == t) return 1;
            }
        }
        return 0;
    }
    int dinic(int x, int flow) {
        if (x == t) return flow;
        int rest = flow, k;
        for (int &i = now[x], y = to[i]; i && rest; y = to[i = ne[i]]) if (co[i] && d[y] == d[x] + 1)
            if (!(k = dinic(y, min(rest, co[i])))) d[y] = 0;
            else co[i] -= k, co[i ^ 1] += k, rest -= k;
        return flow - rest;
    }
    

    最大流关键边

    即参与网络存在(s)(u), (v)(t)且边((u, v))无流量, 则边((u, v))为关键边, 即从(s,t)求bfs可达点

    bool vs[N], vt[N];
    void dfs(int x, bool *v, bool k) {
        v[x] = 1;
        for (int i = h[x], y = to[i]; i; y = to[i = ne[i]])
            if (co[i ^ k] && !v[y]) dfs(y, v, k);
    }
    dfs(s, vs, 0); dfs(t, vt, 1);
    rep (i, 1, m) k += !co[i << 1] && vs[to[i << 1 | 1]] && vt[to[i << 1]];
    

    费用流

    const int N = 5e3 + 5, M = 4e4 + 5, inf = 1 << 30;
    int n, m, _, k;
    int h[N], to[M], ne[M], co[M], ed[M], tot;
    int v[N], incf[N], pre[N], s, t, maxflow, d[N], ans;
    void add(int u, int v, int e, int c) {
        ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c, ed[tot] = e;
        ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = -c; ed[tot] = 0;
    }
    bool bfs() {
        rep (i, 1, n) v[i] = 0, d[i] = -inf;
        queue<int> q; q.push(s); v[s] = 1; d[s] = 0;
        incf[s] = inf; //增广路上各边的最小剩余容量
        while (!q.empty()) {
            int x = q.front(); q.pop(); v[x] = 0;
            for (int i = h[x], y = to[i]; i; y = to[i = ne[i]]) {
                if (!ed[i] || d[y] >= d[x] + co[i]) continue;
                d[y] = d[x] + co[i]; pre[y] = i;
                incf[y] = min(incf[x], ed[i]);
                if (!v[y]) q.push(y), v[y] = 1;
            }
        }
        return d[t] != -inf;
    }
    void update() {
        for (int x = t, i = pre[x]; x != s; i = pre[x = to[i ^ 1]])
            ed[i] -= incf[t], ed[i ^ 1] += incf[t];
        maxflow += incf[t]; ans += d[t] * incf[t];
    }
    int main() {
        IOS; while (cin >> n >> k) { while (bfs()) update(); cout << ans << '
    '; }
        return 0;
    }
    

    无源汇上下界网络流

    int d[N], s, t, now[N], indeg[N], outdeg[N];
    int h[N], ne[M], to[M], co[M], tot, ls[M >> 1];
    
    void add(int u, int v, int c) { 
        ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
        ne[++tot] = h[v]; to[h[v] = tot] = u; co[tot] = 0;
    }
    
    bool bfs() {
        memset(d, 0, sizeof d); memcpy(now, h, sizeof h);
        queue<int> q; q.push(s); d[s] = 1;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = h[x], y =  to[i]; i; y = to[i = ne[i]])
                if (!d[y] &&  co[i]) {
                    d[y] = d[x] + 1; q.push(y);
                    if (y == t) return 1;
                }
        }
        return 0;
    }
    
    int dinic(int x, int flow) {
        if (x == t) return flow;
        int res = flow, k;
        for (int i = now[x], y = to[i]; i && res; now[x] = i, y = to[i = ne[i]])
            if (d[y] == d[x] + 1 && co[i])
                if (!(k = dinic(y, min(res, co[i])))) d[y] = 0;
                else res -= k, co[i] -= k, co[i ^ 1] += k;
        return flow - res;
    }
    
    int main() {
        IOS; cin >> n >> m; tot = 1; s = 0, t = n + 1;
        rep (i, 1, m) {
            int u, v, x, y; cin >> u >> v >> x >> y;
            add(u, v, y - x); ls[i] = x;
            indeg[v] += x; outdeg[u] += x;
        }
        rep (i, 1, n)
            if (outdeg[i] > indeg[i]) add(i, t, outdeg[i] - indeg[i]);
            else if (outdeg[i] < indeg[i]) add(s, i, indeg[i] - outdeg[i]);
        while (bfs()) while (dinic(s, inf));
        for (int i = h[s]; i; i = ne[i])
            if (co[i]) return cout << "NO", 0;
        cout << "YES
    ";
        rep (i, 1, m) cout << ls[i] + co[i << 1 | 1] << '
    ';
        return 0;
    }
    

    有源汇上下界网络流

    把给定的汇点向原点添加一条[(0), (infty)]边, 并把给定的原汇点编程普通点, 变成无源汇

    有源汇上下界最大流

    先按有源汇上下界网络流使得流量平衡, 再把源汇点变为提题目给定的源汇点, 删除汇点间的w无穷边, 跑最大流即可

    int main() {
        IOS; cin >> n >> m >> S >> T; tot = 1; s = 0, t = n + 1;
        rep (i, 1, m) {
            int u, v, x, y; cin >> u >> v >> x >> y;
            add(u, v, y - x); ls[i] = x;
            indeg[v] += x; outdeg[u] += x;
        }
        add(T, S, inf);
        rep (i, 1, n)
            if (outdeg[i] > indeg[i]) add(i, t, outdeg[i] - indeg[i]);
            else if (outdeg[i] < indeg[i]) add(s, i, indeg[i] - outdeg[i]);
        while (bfs()) while (dinic(s, inf));
        for (int i = h[s]; i; i = ne[i])
            if (co[i]) return cout << "No Solution", 0;
        int flow, mxflow = co[m + 1 << 1 | 1]; s = S, t = T;
        co[m + 1 << 1] = co[m + 1 << 1 | 1] = 0;
        while (bfs()) while (flow = dinic(s, inf)) mxflow += flow;
        cout << mxflow;
        return 0;
    }
    

    有源汇上下界最小流

    同有源汇上下界最大流, 只不过变换之后, 是给定的源点变成汇点, 汇点变成源点
    将流量退回去, 即给定的源汇点附加的无穷边反边的流量 减去 退回的流量
    即最大流榨干参与网络, 最小流退回参与网络

    int flow, mxflow = co[m + 1 << 1 | 1]; s = T, t = S;
    co[m + 1 << 1] = co[m + 1 << 1 | 1] = 0;
    while (bfs()) while (flow = dinic(s, inf)) mxflow -= flow;
    cout << mxflow;
    

    字符串

    环的最大最小表示,

    int get(char s[]) { //先复制一倍
        int i = 0, j = 1, k = 0, t;
        while (i < len && j < len && k < len) {
            t = s[(i + k) % len] - s[(j + k) % len];
            if (t == 0) ++k;
            else {
                if (t > 0) i += k + 1; //最大表示 t < 0
                else j += k + 1;
                if (i == j) ++j; k = 0;  
            }
        } return i > j ? j : i;
    }
    

    字符串哈希

    ac自动机(trie树)

    struct AC {
        static const int N = 1e5 + 5, M = 26, C = 'a'; //字符串总长度, 字符范围
        int trie[N][M], cnt[N], fail[N], q[N], tot;
        vector<VI> idx; //记录节点结尾的字符串id
        void init() {
            rep (i, 0, M - 1) trie[0][i] = 0;
            tot = 0; idx.resize(1, VI());
        }
        int newnode() {
            cnt[++tot] = 0; fail[tot] = 0; memset(trie[tot], 0, sizeof trie[tot]);
            return idx.pb(VI()), tot;
        }
        void insert(char* s, int id) {
            int p = 0;
            for (int i = 0, ch = s[i] - C; s[i]; p = trie[p][ch], ch = s[++i] - C)
                if (!trie[p][ch]) trie[p][ch] = newnode();
            ++cnt[p]; idx[p].pb(id);
        }
        void build() {
            int head = 0, tail = -1;
            rep (i, 0, M - 1) if (trie[0][i]) q[++tail] = trie[0][i];
            for (int p = q[head]; head <= tail; p = q[++head]) rep (i, 0, M - 1)
                if (trie[p][i])
                    fail[trie[p][i]] = trie[fail[p]][i], q[++tail] = trie[p][i];
                else trie[p][i] = trie[fail[p]][i];
        }
        int query(char* s) {
            set<int> vis; int res = 0;
            for (int i = 0, p = trie[0][s[i] - C]; s[i]; p = trie[p][s[++i] - C])
                for (int tmp = p; tmp && !vis.count(tmp); tmp = fail[tmp])
                    res += cnt[tmp], vis.insert(tmp);
            return res;
        }
    } ac;
    

    kmp

    char t[LenT], s[LenS];
    int lens, lent, cnt, f[LenT], extend[LenS];
    void KMP() { //可根据f数组建立出sam的子集自动机(f[i]是endpos等价类子集)
        for (int i = 2, j = f[1] = 0; i <= lent; ++i) {
            while (j > 0 && t[i] != t[j + 1]) j = f[j];
            if (t[i] == t[j + 1]) ++j; f[i] = j;
        }
    }
    void ext_KMP() { //也可以直接 t = t + '#' + s; 直接kmp也行
        for (int i = 1, j = extend[1] = 0; i <= lens; ++i) {
            while (j > 0 && (j == lent || s[i] != t[j + 1])) j = f[j];
            extend[i] = s[i] == t[j + 1] ? ++j : j; cnt += (j == lent)
        }
    }
    

    Z函数(扩展KMP)

    int lens, lent, f[N], extend[N];
    char s[N], t[N];
    void kmp(char* t, int lent) { //t从1开始
        int j = 0, k = 2;
        while (j + 2 <= lent && t[j + 1] == t[j + 2]) ++j;
        f[2] = j; f[1] = lent;
        for (int i = 3, p = k + f[k] - 1; i <= lent; ++i, p = k + f[k] - 1)
            if (i + f[i - k + 1] - 1 < p) f[i] = f[i - k + 1];
            else {
                j = max(0, p - i + 1);
                while (j + i <= lent && t[j + 1] == t[i + j]) ++j;
                f[i] = j; k = i;
            }
    }
    void ex_kmp(char *s, char *t, int lens, int lent) { //s, t下标都是从1开始
        int j = 0, k = 1;
        while (j + 1 <= min(lens, lent) && s[j + 1] == t[j + 1]) ++j;
        extend[1] = j;
        for (int i = 2, p = k + extend[k] - 1; i <= lens; ++i, p = k + extend[k] - 1)
            if (i + f[i - k + 1] - 1 < p) extend[i] = f[i - k + 1];
            else {
                j = max(0, p - i + 1);
                while (j + i <= lens && j + 1 <= lent && t[j + 1] == s[i + j]) ++j;
                extend[i] = j; k = i;
            }
    }
    int main() {
        IOS; cin >> s + 1 >> t + 1;
        lent = strlen(t + 1); lens = strlen(s + 1);
        kmp(t, lent); ex_kmp(s, t, lens, lent);
        rep (i, 1, lens) cout << extend[i] << ' ';
        return 0;
    }
    

    manachar

    int pArr[N << 1];
    char s[N], chaArr[N << 1];
    int maxLcsplength(char *s) {
        int len = 0, R = -1, C = -1, maxN = 0; chaArr[len++] = '$', chaArr[len++] = '#';
        for (register int i = 0; s[i]; ++i) chaArr[len++] = s[i], chaArr[len++] = '#';
        chaArr[len] = '';
        for (register int i = 0; i < len; ++i) {
            pArr[i] = R > i ? min(R - i, pArr[(C << 1) - i]) : 1;
            while (chaArr[i + pArr[i]] == chaArr[i - pArr[i]]) ++pArr[i];
            if (i + pArr[i] > R) R = i + pArr[i], C = i; maxN = max(maxN, pArr[i]);
        } return maxN - 1;
    }
    

    序列自动机

    struct SqAM {
        static const int N = 2e3 + 5, M = 26, C = 'a';
        struct Node { int fa, ne[26]; } tr[N << 1]; 
        int rt, tot, lst[M];
        int newNode() { return memset(tr[++tot].ne, 0, sizeof tr[0].ne), tot; }
        void init() { tot = 0; rep (i, 0, M - 1) lst[i] = 1; rt = newNode(); }
        void insert(int ch) {
            int p = lst[ch], cur = newNode(); tr[cur].fa = p;
            rep (i, 0, M - 1) for (int j = lst[i]; j && !tr[j].ne[ch]; j = tr[j].fa)
                tr[j].ne[ch] = cur;
            lst[ch] = cur;
        }
        void build(char* s) { for (int i = 0; s[i]; insert(s[i++] - C)); }
        bool find(char* s) {
            int p = 1;
            for (int i = 0; p && s[i]; p = tr[p].ne[s[i++] - C]);
            return p;
        }
    };
    

    后缀

    前缀在整个串中出现的次数, sam处理完后遍历原串输出cnt即可
    重复可重叠最长子串 自动机(max tr[tr[i].fa].len)
    重复不可重叠最长子串 数组(二分, rk连续的一段且长度>=mid, 这段rk连续的sa位置最大最小值>mid)
    重复k次的可重叠最长子串 自动机(cnt计数, 给fa节点打标记, 在cnt >= k && fa 节点取max len)
    子串个数(相同字串不同位置算1/多个) 自动机
    n个字串lca, sam上跑n次match, 取max ans[i]
    两个后缀最长的lca,两个节点在parent树上的lca
    重复次数最多的连续重复子串(某子串在某个长子串中不重叠出现次数最多, 求的是这个长子串),后缀数组

    void solve() {
        Max = 0;
        for (int i = 1; i <= a.len; ++i)
            for (int j = 1; j + i <= a.len; j += i) {
                ans = a.rmq_query(j, j + i); k = j - (i - ans % i); ans = ans / i + 1;
                if (k >= 1 && a.rmq_query(k, k + i) >= i) ++ans;
                if (Max < ans) Max = ans, cnt = 0, q[cnt++] = i;
                else if (Max == ans && i != q[cnt - 1]) q[cnt++] = i;
            }
        //输出字典序最小的
        for (int i = 1; i <= a.len; ++i)
            for (int j = 0; j < cnt; ++j)
                if (a.rmq_query(a.sa[i], a.sa[i] + q[j]) >= q[j] * (Max - 1)) {
                    a.s[a.sa[i] + q[j] * Max] = '';
                    printf("%s
    ", a.s + a.sa[i]); return;
                }
    }
    

    后缀自动机

    struct SAM { //不管是不是多组数据都调用init
        static const int N = 5e5 + 5, M = 26, C = 'a';
        struct node { int fa, len, ne[M]; } tr[N << 1];
        int sz, las, len, c[N], rk[N << 1], cnt[N << 1];//(i~len)有cnt[i]个字母a[i]
        int sum[N << 1]; //排名为i的节点为头包含的字串数量
        int ans[N << 1], f[N << 1];
        void init() {
            rep (i, 1, sz)
                tr[i].len = tr[i].fa = c[i] = 0, memset(tr[i].ne, 0, sizeof tr[i].ne);
            sz = las = 1; len = 0;
        }
        void add(int ch) {
            int p = las, cur = las = ++sz;
            tr[cur].len = tr[p].len + 1; ++cnt[cur];
            for (; p && !tr[p].ne[ch]; p = tr[p].fa) tr[p].ne[ch] = cur;
            if (p == 0) { tr[cur].fa = 1; return; }
            int q = tr[p].ne[ch];
            if (tr[q].len == tr[p].len + 1) { tr[cur].fa = q; return; }
            int nq = ++sz; tr[nq] = tr[q]; tr[nq].len = tr[p].len + 1;
            for (; p && tr[p].ne[ch] == q; p = tr[p].fa) tr[p].ne[ch] = nq;
            tr[q].fa = tr[cur].fa = nq;
        }
        void build(char *s) {
            for (int& i = len; s[i]; ++i) add(s[i] - C);
        }
        void sort() {
            rep (i, 1, sz) c[i] = 0;
            rep (i, 1, sz) ++c[tr[i].len];
            rep (i, 1, len) c[i] += c[i - 1];
            rep (i, 1, sz) rk[c[tr[i].len]--] = i;
        }
        void getSizeLen(bool f) {
            per (i, sz, 2) //未考虑被压缩的字串(只出现过1次, 且不是原串的前缀)
                if (!f) cnt[rk[i]] = 1; //不同位置的相同字串算一个
                else cnt[tr[rk[i]].fa] += cnt[rk[i]]; //不同位置的相同字串算多个
            per (i, sz, 1) { //忽略tr[1]的大小
                sum[rk[i]] = i == 1 ? 0 : cnt[rk[i]];
                rep (j, 0, M - 1) if (tr[rk[i]].ne[j]) sum[rk[i]] += sum[tr[rk[i]].ne[j]];
            }
        }
        //t匹配s每个位置最大长度, 求多个串再此位置的最大值,就umin(ans[i],f[i]), 初始化ans=max
        int match(char *s) {
            int lenx = 0, p = 1, tmp = 0, mx = 0;
            memset(f, 0, sizeof f);
            for(int& i = lenx, ch = s[i] - C; s[i]; ch = s[++i] - C) {
                if (tr[p].ne[ch]) p = tr[p].ne[ch], ++tmp;
                else {
                    while (!tr[p].ne[ch] && p) p = tr[p].fa;
                    if(p == 0) p = 1, tmp = 0;
                    else tmp = tr[p].len + 1, p = tr[p].ne[ch];
                } umax(f[p], tmp);
            }
            per (i, sz, 1) { p = tr[rk[i]].fa; umax(f[p], min(f[rk[i]], tr[p].len)); }
            rep (i, 2, sz) umin(ans[i], f[i]), umax(mx, ans[i]);
            return mx; 
        }
        void dfssub(int u) { //每个节点可以向下延申字串的数量,考虑被压缩的字串
            if (sum[u]) return; sum[u] = 1;
            for (int i = 0, v; i < M; ++i)
                if (v = tr[u].ne[i]) dfssub(v), sum[u] += sum[v];
        }
        //寻找字串中排名第x的字串,直接进来1, tr[1]空串排名0
        int kth(int k, char *s) {
            //memset(f, 0, sizoe f);
            if (sum[1] < k) return s[0] = '', 0;
            int cur = 1, len = 0;
            while (k) rep (i, 0, M - 1) if (tr[cur].ne[i]) {
                int v = tr[cur].ne[i];
                if (sum[v] >= k) { s[len++] = C + i; cur = v; k -= cnt[v]; break; }
                else k -= sum[v];
            } return s[len] = '', len;
        }
    } sam;
    

    后缀数组

    struct SA {
        static const int N = 30000 + 9;
        char str[N]; //sa[i]表示排名i的后缀起始下标,rk[i]表示起始下标i后缀的排名
        int sa[N], rk[N], tp[N], tax[N], lcp[N], len, f[N][30], M, lg[N];
        inline void sort() {
            memset(tax, 0, (M + 1) * sizeof(int));
            rep (i, 1, len) ++tax[rk[i]];
            rep (i, 1, M) tax[i] += tax[i - 1];
            per (i, len, 1) sa[tax[rk[tp[i]]]--] = tp[i];
        }
        void getH() {
            for (int i = 1, j, k = 0; i <= len; lcp[rk[i++]] = k) {
                if (k) --k; j = sa[rk[i] - 1];
                while (str[i + k] == str[j + k]) ++k;
            }
        }
        void SuffixSort() { //字符串下标从1开始
            M = 200; len = 1;
            for (int& i = len; str[i]; ++i) rk[i] = str[i], tp[i] = i;
            --len; sort();
            for (int w = 1, p = 0; p < len; w <<= 1, M = p) {
                p = 0;
                rep (i, 1, w) tp[++p] = len - w + i;
                rep (i, 1, len) if (sa[i] > w) tp[++p] = sa[i] - w;
                sort(); swap(tp, rk); rk[sa[1]] = p = 1;
                rep (i, 2, len)
                    rk[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]]
                        && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
            } getH();
        }
        void rmq_init() {
            memset(f, 63, sizeof f); lg[1] = 0;
            rep (i, 2, len) lg[i] = lg[i + 1 >> 1] + 1;
            rep (i, 0, len - 1) f[i][0] = lcp[i + 1];
            for (int j = 1, mj = 2; mj <= len; ++j, mj <<= 1) rep (i, 1, len - mj)
                    f[i][j] = min(f[i][j - 1], f[i + (mj >> 1)][j - 1]);
        }
        int rmq_query(int l, int r) {
            if (l < 1 || r > len) return 0;
            if (l == r) return len - l + 1;
            l = rk[l], r = rk[r];
            if (l > r) swap(l, r);
            return min(f[l][lg[r - l] - 1], f[r - k][lg[r - l] - 1]);
        }
    } sa;
    

    树上SA

    struct SA { //树上后缀数组要用倍增, 叶子节点到根节点是一个字符串, 跟一般不同
        static const int N = 5e5 + 9;
        char str[N]; //sa[i]表示排名i的后缀起始下标,rk[i]表示起始下标i后缀的排名
        int sa[N], rk[N], tp[N], tax[N], len;
        int rk2[N], rkk[N]; //树上sa新增
        inline void sort(int* sa, int* rk, int* tp, int M) { //要排两次序
            memset(tax, 0, (M + 1) * sizeof(int));
            rep(i, 1, len) ++tax[rk[i]];
            rep(i, 1, M) tax[i] += tax[i - 1];
            per(i, len, 1) sa[tax[rk[tp[i]]]--] = tp[i];
        }
        void SuffixSort() { //俩个串相同比较父亲串,再比较这俩串的节点
            int p; len = 1;
            for (int& i = len; str[i]; ++i) rk2[i] = str[i] - 'a' + 1, tp[i] = i;
            --len; sort(sa, rk2, tp, 30); rk[sa[1]] = rkk[sa[1]] = p = 1;
            rep(i, 2, len) {
                rk[sa[i]] = rk2[sa[i - 1]] == rk2[sa[i]] ? p : ++p;
                rkk[sa[i]] = i;
            }
            for (int w = 1, t = 0; w < len; w <<= 1, ++t) {
                rep(i, 1, len) rk2[i] = rkk[ST.f[i][t]];
                sort(tp, rk2, sa, len); sort(sa, rk, tp, p);
                swap(rk, tp); rk[sa[1]] = rkk[sa[1]] = p = 1;
                rep(i, 2, len) {
                    rk[sa[i]] = tp[sa[i - 1]] == tp[sa[i]]
                        && tp[ST.f[sa[i - 1]][t]] == tp[ST.f[sa[i]][t]] ? p : ++p;
                    rkk[sa[i]] = i;
                }
            }
            rep(i, 1, len) rk[i] = rkk[i];
        }
    } sa;
    

    广义后缀自动机

    离线

    struct EXSAM { //trie一样的空间 N * M, 再建自动机为 N * M << 1
        static const int N = 1e5 + 5, M = 26, C = 'a', NUM = 15;//N字串总长度,NUM字符串个数
        struct Node { int len, fa, ne[M]; } tr[N << 1]; //根据情况增大N,把c数组删了
        int tot, mxlen, curString, cnt[N << 1][NUM], tax[N << 1], rk[N << 1];
        int newNode() { return memset(tr[++tot].ne, 0, sizeof tr[0].ne), tot; }
        void init() { 
            memset(tr[0].ne, 0, sizeof tr[0].ne); 
            tot = mxlen = 0; tr[0].fa = -1;
        } //离线并且要对多个字串公共操作, 很难将数组cnt开好,只给你总长不给NUM很难受,在线好
        int insertSAM(int las, int ch) {
            int cur = tr[las].ne[ch], p = tr[las].fa;
            tr[cur].len = tr[las].len + 1;
            for (; p != -1 && !tr[p].ne[ch]; p = tr[p].fa) tr[p].ne[ch] = cur;
            if (p == -1) return tr[cur].fa = 0, cur;
            int q = tr[p].ne[ch];
            if (tr[p].len + 1 == tr[q].len) return tr[cur].fa = q, cur;
            int nq = ++tot; tr[nq].len = tr[p].len + 1; tr[nq].fa = tr[q].fa;
            rep (i, 0, M - 1) tr[nq].ne[i] = tr[tr[q].ne[i]].len ? tr[q].ne[i] : 0;
            for (; p != -1 && tr[p].ne[ch] == q; p = tr[p].fa) tr[p].ne[ch] = nq;
            return tr[cur].fa = tr[q].fa = nq, cur;
        }
        int insertTrie(int cur, int ch) {
            if (!tr[cur].ne[ch]) tr[cur].ne[ch] = newNode();
            ++cnt[tr[cur].ne[ch]][curString];
            return tr[cur].ne[ch];
        }
        void insert(const string& s) {
            for (int i = 0, p = 0; i < s.size(); p = insertTrie(p, s[i++] - C));
            umax(mxlen, s.size()); ++curString; //curString下标从0开始
        }
        void insert(const char *s) {
            int len = 0;
            for (int& i = len, p = 0; s[i]; p = insertTrie(p, s[i++] - C));
            umax(mxlen, len - 1); ++curString; //curString下标从0开始
        }
        void build() {
            queue<pair<int, int>> q;
            rep (i, 0, M - 1) if (tr[0].ne[i]) q.push({ i, 0 });
            while (!q.empty()) {
                PII it = q.front(); q.pop();
                int las = insertSAM(it.se, it.fi);
                rep (i, 0, M - 1) if (tr[las].ne[i]) q.push({ i, las });
            }
        }
        void sort() {
            rep (i, 1, tot) tax[i] = 0;
            rep (i, 1, tot) ++tax[tr[i].len];
            rep (i, 2, mxlen) tax[i] += tax[i - 1];
            rep (i, 1, tot) rk[tax[tr[i].len]--] = i;
        }
        void getSizeLen() {
            per(i, tot, 1)rep(j, 0, curString - 1)cnt[tr[rk[i]].fa][j] += cnt[rk[i]][j];
        }
        int mxComlca() { //最长公共子串
            int ans = 0;
            for (int i = 0, flag = 1; i <= tot; ++i) {
                rep (j, 0, curString - 1) if (!cnt[i][j]) { flag = 0; break; }
                if (flag) umax(ans, tr[i].len);
            }
            return ans;
        }
        ll difsub() { //不同位置相同字串算1个
            ll ans = 0;
            rep (i, 1, tot) ans += tr[i].len - tr[tr[i].fa].len;
            return ans;
        }
        int tag[N << 1], cnt[N];//cnt记录每个节点经过多少个字串
        void dfs(int p, int id) {
            for (; p != -1 && tag[p] != id; p = tr[p].fa) tag[p] = id, ++cnt[p];
        }
        void cntLen(char *s, int* len, int n) {
            rep (i, 1, n)
               for(int j=len[i-1],p=tr[0].ne[s[j]-C];j<len[i];p=tr[p].ne[s[++j]-C])dfs(p,i);
        } //s存n个字串,len第i个字串结尾的位置
    } exSam;
    

    回文自动机

    struct PAM { //当len[i] > 0才是真实串(len[0]=0,len[1]=-1)
        static const int N = 260817, M = 26, C = 'a';
        struct Node { int ne[M], len, fail, cnt; } tr[N];
        int sz, tot, last;
        char s[N];
        int newNode(int l) {
            memset(tr[++sz].ne, 0, sizeof(tr[0].ne)); tr[sz].len = l;
            return tr[sz].fail = tr[sz].cnt = 0, sz;
        }
        void init() {
            sz = -1; last = 0; s[tot = 0] = '$';
            newNode(0); newNode(-1); tr[0].fail = 1;
        }
        int getfail(int x) {
            while (s[tot - tr[x].len - 1] != s[tot]) x = tr[x].fail;
            return x;
        }
        void insert(char c) {
            s[++tot] = c; int now = getfail(last), ch = c - C;
            if (!tr[now].ne[ch]) {
                int x = newNode(tr[now].len + 2);
                tr[x].fail = tr[getfail(tr[now].fail)].ne[ch];
                tr[now].ne[ch] = x;
            }
            ++tr[last = tr[now].ne[ch]].cnt;
        }
        void build(char *s) {
            for (int i = 0; s[i]; ++i) insert(s[i]);
            per (i, sz, 0) tr[tr[i].fail].cnt += tr[i].cnt;
        }
        ll solve() {
            ll ans = 0;
            rep (i, 1, sz) umax(ans, (ll)tr[i].len * tr[i].cnt);
            return ans;
        }
    } pam;
    

    Dancing linke

    行为决策, 列为影响

    数独

    先考虑决策是什么。

    在这一题中,每一个决策可以用形如((r,c,w))的有序三元组表示。

    注意到“宫”并不是决策的参数,因为它可以被每个确定的((r,c))表示

    因此有(9 imes 9 imes 9 = 729)行。

    再考虑状态是什么。

    我们思考一下((r,c,w))这个决将会造成什么影响。记((r,c))所在的宫为(b)

    1. (r)行用了一个(w)(用(9 imes 9 = 81)列表示);
    2. (c)列用了一个(w)(用(9 imes 9 = 81)列表示);
    3. (b)宫用了一个 (用(9 imes 9 = 81)列表示);
    4. ((r,c))中填入了一个数(用(9 imes 9 = 81)列表示)。
      因此有(81 * 4 = 324)列,共(729 imes 4 = 2916)(1)

    至此,我们成功地将(9 imes 9)的数独问题转化成了一个 有(729)行,(324)列,共(2916)(1)的精确覆盖问题。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct DLX {
        static const int N = 1e5 + 5;
    #define IT(i, A, x) for(int i=A[x];i^x;i=A[i])
        int n, m, tot, first[N], siz[N], stk[N], ans;
        int L[N], R[N], U[N], D[N], col[N], row[N];
        void build(const int &r, const int &c) {
            for (int i = 0; i <= c; ++i) L[i] = i - 1, R[i] = i + 1, U[i] = D[i] = i;
            n = r, m = c; L[0] = c, R[c] = 0, tot = c;
            memset(first, 0, sizeof(first)); memset(siz, 0, sizeof(siz));
        }
        void insert(const int &r, const int &c) {
            col[++tot] = c, row[tot] = r, ++siz[c];
            D[tot] = D[c], U[D[c]] = tot, U[tot] = c, D[c] = tot;
            if (!first[r]) first[r] = L[tot] = R[tot] = tot;
            else {
                R[tot] = R[first[r]], L[R[first[r]]] = tot;
                L[tot] = first[r], R[first[r]] = tot;
            }
        }
        void remove(const int &c) {
            L[R[c]] = L[c], R[L[c]] = R[c];
            IT(i, D, c) IT(j, R, i) U[D[j]] = U[j], D[U[j]] = D[j], --siz[col[j]];
        }
        void recover(const int &c) {
            IT(i, U, c) IT(j, L, i) U[D[j]] = D[U[j]] = j, ++siz[col[j]];
            L[R[c]] = R[L[c]] = c;
        }
        bool dance(int dep) {
            if (!R[0]) return ans = dep, 1;
            int c = R[0];
            IT(i, R, 0) if (siz[i] < siz[c]) c = i;
            remove(c);
            IT(i, D, c) {
                stk[dep] = row[i];
                IT(j, R, i) remove(col[j]);
                if (dance(dep + 1)) return 1;
                IT(j, L, i) recover(col[j]);
            }
            recover(c);
            return 0;
        }
    #undef IT
    } dlx;
    
    int a[10][10];
    
    void insert(int r, int c, int n) {
        int g = (r - 1) / 3 * 3 + (c - 1) / 3 + 1;
        int id = (r - 1) * 81 + (c - 1) * 9 + n;
        dlx.insert(id, (r - 1) * 9 + n);
        dlx.insert(id, 81 + (c - 1) * 9 + n);
        dlx.insert(id, 162 + (g - 1) * 9 + n);
        dlx.insert(id, 243 + (r - 1) * 9 + c);
    }
    
    int main() {
        string s;
        while (cin >> s, s != "end") {
            dlx.build(729, 324);
            for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) {
                a[i][j] = s[(i - 1) * 9 + j - 1] == '.' ? 0 : s[(i - 1) * 9 + j - 1] ^ '0';
                for (int v = 1; v <= 9; ++v) if (!a[i][j] || a[i][j] == v) insert(i, j, v);
            }
            dlx.dance(0);
            for (int i = 0; i < dlx.ans; ++i)
                a[(dlx.stk[i] - 1) / 81 + 1][(dlx.stk[i] - 1) / 9 % 9 + 1] = (dlx.stk[i] - 1) % 9 + 1;
            for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) cout << a[i][j]; cout << '
    ';
        }
      return 0;
    }
    

    靶形数独

    这一题与数独的模型构建 一模一样,主要区别在于答案的更新。

    这一题可以开一个权值数组,每次找到一组数独的解时,

    每个位置上的数乘上对应的权值计入答案即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct DLX {
        static const int N = 1e5 + 5;
    #define IT(i, A, x) for(int i=A[x];i^x;i=A[i])
        int n, m, tot, first[N], siz[N], stk[N], ans;
        int L[N], R[N], U[N], D[N], col[N], row[N], mx, w[N];
        void build(const int &r, const int &c) {
            for (int i = 0; i <= c; ++i) L[i] = i - 1, R[i] = i + 1, U[i] = D[i] = i;
            n = r, m = c; L[0] = c, R[c] = 0, tot = c;
            memset(first, 0, sizeof(first)); memset(siz, 0, sizeof(siz));
        }
        void insert(const int &r, const int &c, const int &W) {
            col[++tot] = c, row[tot] = r, w[tot] = W, ++siz[c];
            D[tot] = D[c], U[D[c]] = tot, U[tot] = c, D[c] = tot;
            if (!first[r]) first[r] = L[tot] = R[tot] = tot;
            else {
                R[tot] = R[first[r]], L[R[first[r]]] = tot;
                L[tot] = first[r], R[first[r]] = tot;
            }
        }
        void remove(const int &c) {
            L[R[c]] = L[c], R[L[c]] = R[c];
            IT(i, D, c) IT(j, R, i) U[D[j]] = U[j], D[U[j]] = D[j], --siz[col[j]];
        }
        void recover(const int &c) {
            IT(i, U, c) IT(j, L, i) U[D[j]] = D[U[j]] = j, ++siz[col[j]];
            L[R[c]] = R[L[c]] = c;
        }
        bool dance(int dep, int cur) {
            if (!R[0]) return mx = max(cur, mx), 1;
            int c = R[0];
            IT(i, R, 0) if (siz[i] < siz[c]) c = i;
            remove(c);
            IT(i, D, c) {
                stk[dep] = row[i];
                IT(j, R, i) remove(col[j]);
                dance(dep + 1, cur + w[i]);
                IT(j, L, i) recover(col[j]);
            }
            recover(c);
            return 0;
        }
    #undef IT
    } dlx;
    
    int a[10][10];
    
    void insert(int r, int c, int n, int w) {
        int g = (r - 1) / 3 * 3 + (c - 1) / 3 + 1;
        int id = (r - 1) * 81 + (c - 1) * 9 + n;
        dlx.insert(id, (r - 1) * 9 + n, w);
        dlx.insert(id, 81 + (c - 1) * 9 + n, w);
        dlx.insert(id, 162 + (g - 1) * 9 + n, w);
        dlx.insert(id, 243 + (r - 1) * 9 + c, w);
    }
    
    int main() {
        dlx.build(729, 324);
        for (int i = 1; i <= 9; ++i) for (int j = 1; j <= 9; ++j) {
            cin >> a[i][j]; int d = max(abs(i - 5), abs(j - 5));
            for (int v = 1; v <= 9; ++v) if (!a[i][j] || a[i][j] == v) insert(i, j, v, (10 - d) * v);
        }
        dlx.dance(0, 0); cout << (dlx.mx ? dlx.mx : -1);
        return 0;
    }
    
  • 相关阅读:
    POJ 1659 Frogs' Neighborhood
    zoj 2913 Bus Pass(BFS)
    ZOJ 1008 Gnome Tetravex(DFS)
    POJ 1562 Oil Deposits (DFS)
    zoj 2165 Red and Black (DFs)poj 1979
    hdu 3954 Level up
    sgu 249 Matrix
    hdu 4417 Super Mario
    SPOJ (BNUOJ) LCM Sum
    hdu 2665 Kth number 划分树
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/15451069.html
Copyright © 2011-2022 走看看