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;
    }
    
  • 相关阅读:
    asp.net 文件下载
    net 数据库连接详解 相当经典啊
    取值:webconfig中的key
    通过监听的端口查找本机进程和服务思路
    以系统服务运行某个程序
    测底根除Windows流氓软件开机自动运行
    使用Wireshark在主机上抓取远程主机的数据流量
    记录Windows远程登录日志
    证书不匹配发出告警的解决方法
    WPS office云同步图标彻底删除方法
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/15451069.html
Copyright © 2011-2022 走看看