zoukankan      html  css  js  c++  java
  • LCT学习笔记

    [HNOI2010]弹飞绵羊

    可以省去换根之类的操作

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + 1)
    
    using namespace std;
    
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    inline void write(int x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 2e5 + 10;
    
    int n, m, fa[N], tr[N][2], sze[N];
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    
    void splay(int x) {
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x);
    }
    
    int main() {
        n = read();
        for (int i = 1; i <= n; i++) {
            int k = read(); sze[i] = 1;
            if (i + k <= n) fa[i] = i + k;
        }
        for (m = read(); m; m--) {
            int opt = read(), st = read() + 1;
            if (opt == 1) access(st), splay(st), write(sze[st], '
    ');
            else {
                int k = read();
                access(st), splay(st), tr[st][0] = fa[tr[st][0]] = 0; // cut
                if (st + k <= n) fa[st] = st + k; // link
            }
        }
        return 0;
    } 

    图上询问

    题意:给定$n$个点$m$条边的无向图,$q$次询问,每次询问编号在$[l_i,r_i]$的边构成多少连通块,$n,m,q leq 2 imes 10^5$

    解法:

      对于$r in [1,n]$,求$[1,r]$的最大生成森林(以边的编号为边权)

      连通块数=点数-最大生成森林的边数

      主席树维护每个前缀$[1,r]$中,每种编号的边是否出现过,即可做到在线解决

      $mathcal O((n + m) log (n+m)+q log m)$

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    
    using namespace std;
    
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    inline void write(int x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 2e5 + 10, M = 2e5 + 10;
    
    int n, m, q, f[N + M], fa[N + M], tr[N + M][2], val[N + M]; bool rev[N + M];
    
    int find(int u) {
        return f[u] == u ? u : f[u] = find(f[u]);
    }
    
    void reverse(int x) {
        swap(tr[x][0], tr[x][1]), rev[x] ^= 1;
    }
    
    void pushdown(int x) {
        if (rev[x]) {
            if (tr[x][0]) reverse(tr[x][0]);
            if (tr[x][1]) reverse(tr[x][1]);
            rev[x] = 0;
        }
    }
    
    void pushall(int x) {
        if (!isroot(x)) pushall(fa[x]);
        pushdown(x);
    }
    
    void updata(int x) {
        val[x] = (x <= n ? 0x3f3f3f3f : x);
        if (tr[x][0] && val[x] > val[tr[x][0]]) val[x] = val[tr[x][0]];
        if (tr[x][1] && val[x] > val[tr[x][1]]) val[x] = val[tr[x][1]]; 
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        updata(y), updata(x);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x);
    }
    
    int findrt(int u) {
        access(u), splay(u);
        while (tr[u][0]) pushdown(u), u = tr[u][0];
        return splay(u), u;
    }
    
    void makert(int u) {
        access(u), splay(u), reverse(u);
    }
    
    void link(int u, int v) {
        makert(u), splay(v), fa[u] = v;
    }
    
    void split(int u, int v) {
        makert(u), access(v), splay(v);
    }
    
    int rt[M], ls[M * 40], rs[M * 40], sum[M * 40], nodeid;
    
    int modify(int pre, int l, int r, int pos, int delta) {
        int now = ++nodeid;
        sum[now] = sum[pre], ls[now] = ls[pre], rs[now] = rs[pre];
        if (l == r) sum[now] += delta;
        else {
            int mid = (l + r) >> 1;
            if (pos <= mid) ls[now] = modify(ls[pre], l, mid, pos, delta);
            else rs[now] = modify(rs[pre], mid + 1, r, pos, delta);
            sum[now] = sum[ls[now]] + sum[rs[now]];
        }
        return now;
    }
    
    int query(int u, int v, int l, int r, int Left, int Right) {
        if (Left <= l && r <= Right) return sum[u] - sum[v];
        int mid = (l + r) >> 1, res = 0;
        if (Left <= mid) res += query(ls[u], ls[v], l, mid, Left, Right);
        if (mid < Right) res += query(rs[u], rs[v], mid + 1, r, Left, Right);
        return res;
    }
    
    int main() {
        freopen("graph.in", "r", stdin);
        freopen("graph.out", "w", stdout);
        n = read(), m = read(), q = read();
        for (int i = 1; i <= n; i++) f[i] = i;
        for (int i = 1; i <= m; i++) {
            rt[i] = rt[i - 1];
            int u = read(), v = read();
            if (u == v) continue;
            if (find(u) != find(v)) f[find(u)] = find(v), link(u, i + n), link(i + n, v);
            else {
                split(u, v);
                int now = val[v];
                splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0;
                link(u, i + n), link(i + n, v);
                rt[i] = modify(rt[i], 1, m, now - n, -1);    
            }
            rt[i] = modify(rt[i], 1, m, i, 1);
        }
        for (int i = 1; i <= q; i++) {
            int l = read(), r = read();
            int tot = query(rt[r], rt[l - 1], 1, m, l, m);
            write(n - tot, '
    ');
        }
        return 0;
    }

    「NOI2014」魔法森林

    #include <bits/stdc++.h>
     
    #define check(x) (tr[fa[x]][1] == x) 
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
     
    using namespace std;
     
    inline int read() {
        int out = 0;
        bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
     
     
    const int N = 5e4 + 5, M = 1e5 + 5;
     
    int n, m, ans, val[N + M], tr[N + M][2], fa[N + M], Max[N + M], id[N + M], f[N];
     
    bool rev[N + M];
     
    struct edge {
        int u, v, a, b;
        void readdata() {
            u = read(), v = read(), a = read(), b = read();
        }
        bool operator < (const edge &g) const {
            return a < g.a;
        }
    } e[M];
     
    int find(int u) {
        return f[u] == u ? u : f[u] = find(f[u]);
    }
     
    void updata(int u) {
        Max[u] = val[u], id[u] = u;
        if (tr[u][0] && Max[u] < Max[tr[u][0]]) Max[u] = Max[tr[u][0]], id[u] = id[tr[u][0]];
        if (tr[u][1] && Max[u] < Max[tr[u][1]]) Max[u] = Max[tr[u][1]], id[u] = id[tr[u][1]];
    }
     
    void reverse(int u) {
        swap(tr[u][0], tr[u][1]), rev[u] ^= 1;
    }
     
    void pushdown(int u) {
        if (rev[u]) {
            if (tr[u][0]) reverse(tr[u][0]);
            if (tr[u][1]) reverse(tr[u][1]);
            rev[u] = 0;
        }
    }
     
    void pushall(int u) {
        if (!isroot(u)) pushall(fa[u]);
        pushdown(u);
    }
     
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        updata(y), updata(x);
    }
     
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x], z = fa[y];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
     
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x);
    }
     
    int findrt(int u) {
        access(u), splay(u);
        while (tr[u][0]) pushdown(u), u = tr[u][0];
        return splay(u), u;
    }
     
    void makert(int u) {
        access(u), splay(u), reverse(u);
    }
     
    void link(int u, int v) {
        makert(v), splay(u), fa[v] = u;
    }
     
    void cut(int u, int v) {
        makert(v), splay(v), fa[u] = tr[v][1] = 0, updata(v);
    }
     
    void split(int u, int v) {
        makert(u), access(v), splay(v);
    }
     
     
    int main() {
        n = read(), m = read(), ans = 0x3f3f3f3f;
        for (int i = 1; i <= m; i++) e[i].readdata();
        sort(e + 1, e + m + 1);
        for (int i = 1; i <= n; i++) f[i] = i;
        for (int i = 1; i <= m; i++) {
            val[i + n] = e[i].b; int u = e[i].u, v = e[i].v;
            if (u == v) continue;
            if (find(u) != find(v)) link(u, i + n), link(i + n, v), f[find(u)] = find(v);
            else {
                split(u, v);
                int now = id[v], b = Max[v];
                if (b <= e[i].b) continue;
                splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0;
                link(u, i + n), link(i + n, v);
            }
            if (find(1) == find(n)) split(1, n), ans = min(ans, Max[n] + e[i].a);
        }
        cout << (ans == 0x3f3f3f3f ? -1 : ans) << endl;
        return 0;
    }

    最小差值生成树

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    
    using namespace std;
    
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    inline void write(int x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    
    const int N = 5e4 + 10, M = 2e5 + 10;
    
    struct edge {
        int u, v, w;
        void readdata() {
            u = read(), v = read(), w = read();
        }
        bool operator < (const edge &g) const {
            return w < g.w;
        }
    } e[M];
    
    int n, m, ans = 0x3f3f3f3f, cnt, f[N], fa[N + M], tr[N + M][2], val[N + M], Min[N + M], id[N + M];
    
    bool vis[M], rev[N + M]; int l;
    
    int find(int u) {
        return f[u] == u ? u : f[u] = find(f[u]);
    }
    
    void updata(int u) {
        Min[u] = val[u], id[u] = u;
        if (tr[u][0] && Min[u] > Min[tr[u][0]]) Min[u] = Min[tr[u][0]], id[u] = id[tr[u][0]];
        if (tr[u][1] && Min[u] > Min[tr[u][1]]) Min[u] = Min[tr[u][1]], id[u] = id[tr[u][1]];
    }
    
    void reverse(int u) {
        swap(tr[u][0], tr[u][1]), rev[u] ^= 1;
    }
    
    void pushdown(int u) {
        if (rev[u]) {
            if (tr[u][0]) reverse(tr[u][0]);
            if (tr[u][1]) reverse(tr[u][1]);
            rev[u] = 0;
        }
    }
    
    void pushall(int u) {
        if (!isroot(u)) pushall(fa[u]);
        pushdown(u);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        updata(y), updata(x);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x);
    } 
    
    void makert(int u) {
        access(u), splay(u), reverse(u);
    }
    
    int findrt(int u) {
        access(u), splay(u);
        while (tr[u][0]) pushdown(u), u = tr[u][0];
        return u;
    }
    
    void link(int u, int v) {
        makert(u), splay(u), fa[u] = v;
    }
    
    void cut(int u, int v) {
        makert(u), splay(u), fa[v] = tr[u][1] = 0, updata(u); 
    }
    
    void split(int u, int v) {
        makert(u), access(v), splay(v);
    }
    
    int main() {
        n = read(), m = read();
        for (int i = 1; i <= m; i++) e[i].readdata();
        sort(e + 1, e + m + 1);
        for (int i = 1; i <= n; i++) f[i] = i, val[i] = 0x3f3f3f3f;
        cnt = n;
        for (int i = 1; i <= m; i++) {
            int u = e[i].u, v = e[i].v; val[i + n] = e[i].w;
            if (u == v) continue;
            if (find(u) != find(v)) f[find(u)] = find(v), cnt--, link(u, i + n), link(i + n, v), vis[i] = true;
            else {
                split(u, v);
                int now = id[v];
                splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0, vis[now - n] = false;
                link(u, i + n), link(i + n, v), vis[i] = true;
            }
            while (!vis[l]) l++;
            if (cnt == 1) ans = min(ans, e[i].w - e[l].w);
        }
        write(ans, '
    ');
        return 0;
    }

     

    魔术师

    逆向考虑

    优先级最低的一定是最后删的

    在这之前是先删优先级次低的到优先级最低的链上的点

    再之前是删优先级第三低的到优先级次低和优先级最低的链上的点

    我们把优先级最低的作为根

    按优先级从高到低,依次将它们到根的路径染上一种比之前都深的颜色(后面的覆盖前面的,染色过程相当于$access$操作)

    发现把某点优先级设为最低也相当于$makeroot$操作

    查询时则是维护$u$所在的实链中深度大于它的点数加上比它浅的实链的总点数

    第一部分$splay$上直接维护,第二部分加个树状数组即可

    有个东西我tmd改到自闭,就树状数组的上界是$n+m$,可是对于多组询问修改,我习惯这样写(((((((

    这是什么傻逼。。。

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + 1)
    
    using namespace std;
    
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    inline void write(int x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 5e5 + 10;
    
    int n, m, fa[N], tr[N][2], sze[N], id[N], tot, sum[N << 1]; bool rev[N];
    
    int head[N], to[N << 1], nxt[N << 1], cnt;
    
    void add_edge(int u, int v) {
        to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
    }
    
    void dfs(int u, int las) {
        fa[u] = las;
        for (int i = head[u]; i; i = nxt[i]) {
            int v = to[i]; 
            if (v == las) continue;
            dfs(v, u);
        }
    }
    
    void add(int k, int delta) {
        for ( ; k <= n + m; k += (k & -k)) sum[k] += delta;
    }
    
    int ask(int k) {
        int res = 0;
        for ( ; k; k ^= (k & -k)) res += sum[k];
        return res;
    }
    
    void reverse(int x) {
        swap(tr[x][0], tr[x][1]), rev[x] ^= 1;
    }
    
    void pushdown(int x) {
        if (rev[x]) {
            if (tr[x][0]) reverse(tr[x][0]);
            if (tr[x][1]) reverse(tr[x][1]);
            rev[x] = false;
        }
        if (tr[x][0]) id[tr[x][0]] = id[x];
        if (tr[x][1]) id[tr[x][1]] = id[x];
    }
    
    void pushall(int x) {
        if (!isroot(x)) pushall(fa[x]);
        pushdown(x);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
        pushup(x);
    }
    
    void access(int x) {
        int d = 0, u = x, y = 0;
        for (y = 0; x; y = x, x = fa[x]) {
            splay(x);
            int s = 1;
            if (tr[x][0]) s += sze[tr[x][0]];
            if (id[x]) add(id[x], -s); pushdown(x);
            d += s, tr[x][1] = y, pushup(x);
        }
        splay(y), add(id[y] = ++tot, d); // 先splay才能保证整条链改 
    }
    
    void makert(int x) {
        access(x), splay(x), reverse(x);
    }
    
    int main() {
        n = read(), m = read();
        for (int i = 1; i < n; i++) {
            int u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
        }
        dfs(n, 0);
        for (int i = 1; i <= n; i++) access(i);
        for (int i = 1; i <= m; i++) {
            char opt = getchar();
            while (opt != 'M' && opt != 'Q') opt = getchar();
            if (opt == 'M') makert(read());
            else {
                int u = read(); splay(u);
                write(ask(id[u]) - sze[tr[u][0]], '
    ');
            }
        }
        return 0;
    } 

    [AHOI2005] 航线规划

    删边不好搞,把操作倒序进行,则删边改为加边

    每次加边就是把$LCT$维护的那棵树上对应两点之间的路径给$split$出来,打个全赋为$0$的标记

    就不写了吧,思路错了别找我哇2333

     

    「BJOI2014」大融合

    类似$DDP$,将虚儿子$size$和记做$s[u]$,总的$size$和记做$sze[u]$,然后转移

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + s[x] + 1)
    
    using namespace std;
    
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    inline void write(long long x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 1e5 + 10;
    
    int n, m, fa[N], tr[N][2], s[N], sze[N]; bool rev[N];
    
    void reverse(int x) {
        swap(tr[x][0], tr[x][1]), rev[x] ^= 1;
    }
    
    void pushdown(int x) {
        if (rev[x]) {
            if (tr[x][0]) reverse(tr[x][0]);
            if (tr[x][1]) reverse(tr[x][1]);
            rev[x] = false;
        }
    }
    
    void pushall(int x) {
        if (!isroot(x)) pushall(fa[x]);
        pushdown(x);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), s[x] += sze[tr[x][1]], s[x] -= sze[tr[x][1] = y]/*, pushup(x)*/;
    }
    
    void makert(int x) {
        access(x), splay(x), reverse(x);
    }
    
    void split(int u, int v) {
        makert(u), access(v), splay(v);
    }
    
    void link(int u, int v) {
        makert(u), access(v), splay(v), s[fa[u] = v] += sze[u], pushup(v);
    } // 注意这里要access,才无需再向上更新 
    
    int main() {
        n = read(), m = read();
        for (int i = 1; i <= n; i++) sze[i] = 1;
        for (int i = 1; i <= m; i++) {
            char opt = getchar();
            while (opt != 'A' && opt != 'Q') opt = getchar();
            int u = read(), v = read();
            if (opt == 'A') link(u, v);
            else {
                split(u, v);
                write((s[u] + 1ll) * (s[v] + 1ll), '
    ');
            }
        }
        return 0;
    }

     

    「SHOI2014」三叉神经树

    每次修改的只有可能是该点往上的一段连续区间

    $val[u]$为$u$儿子中为$1$的节点个数

    记录$splay$所维护实链上深度最大的$val$非$1$节点和$val$非$2$节点

    对链上进行区间改和单点改

    区间改所改的子树只有可能全为$1$或全为$2$,$pushdown$时只需要$swap$深度最大的非$1$节点和非$2$节点

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    
    using namespace std;
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    }
    
    const int N = 5e5 + 10;
    
    int n, fa[3 * N], tr[N][2], deg[N], val[3 * N], id1[N], id2[N], tag[N]; queue<int> q;
    
    void pushup(int x) {
        id1[x] = id2[x] = -1;
        int ls = tr[x][0], rs = tr[x][1];
        if (rs) id1[x] = id1[rs], id2[x] = id2[rs];
        if (id1[x] == -1 && val[x] != 1) id1[x] = x;
        if (id2[x] == -1 && val[x] != 2) id2[x] = x;
        if (ls && id1[x] == -1) id1[x] = id1[ls];
        if (ls && id2[x] == -1) id2[x] = id2[ls]; 
    }
    
    void change(int u, int delta) {
        if (u) tag[u] += delta, val[u] += delta, swap(id1[u], id2[u]);
    }
    
    void pushdown(int u) {
        if (tag[u]) {
            if (tr[u][0]) change(tr[u][0], tag[u]);
            if (tr[u][1]) change(tr[u][1], tag[u]);
            tag[u] = 0;
        }
    }
    
    void pushall(int u) {
        if (!isroot(u)) pushall(fa[u]);
        pushdown(u);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        pushup(y);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
        pushup(x);
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x);
    }
    
    int main() {
        n = read();
        for (int i = 1; i <= n; i++) {
            int a = read(), b = read(), c = read();
            fa[a] = fa[b] = fa[c] = i, deg[i] = 3;
        }
        for (int i = n + 1; i <= 3 * n + 1; i++) val[i] = read(), q.push(i);
        while (q.size()) {
            int u = q.front(), v = fa[u]; q.pop();
            val[v] += (u > n ? val[u] : (int)(val[u] >= 2)), deg[v]--;
            if (deg[v] == 0) q.push(v);
        }
        int ans = (val[1] >= 2);
        for (int T = read(); T; T--) {
            int u = read(), op = (val[u] ^= 1);
            access(u = fa[u]), splay(u);
            if (op) {
                if (id1[u] == -1) change(u, 1), ans ^= 1;
                else splay(u = id1[u]), change(tr[u][1], 1), val[u]++, pushup(u);
            }
            else {
                if (id2[u] == -1) change(u, -1), ans ^= 1;
                else splay(u = id2[u]), change(tr[u][1], -1), val[u]--, pushup(u);
            }
            puts(ans ? "1" : "0");
        }
        return 0;
        
    } 

     

    「SDOI2017」树点涂色

    涂色操作相当于$LCT$的$access$操作

    定义$val[x]$为$x$到根的路径的权值

    路径$x ightarrow y$的权值为$val[x]+val[y]-2 imes val[lca(x,y)]+1$

    维护$val$则考虑$access$时,$u$点的父亲边由虚变实$u$子树$-1$,由实变虚子树$+1$

    注意$LCT$上的父子关系不代表原树上的父子关系

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    
    #define lson u << 1, l, mid
    #define rson u << 1 | 1, mid + 1, r
    
    using namespace std;
    
    inline int read() {
        int out = 0; bool flag = false;
        register char cc = getchar();
        while (cc < '0' || cc > '9') {
            if (cc == '-') flag = true;
            cc = getchar();
        }
        while (cc >= '0' && cc <= '9') {
            out = (out << 3) + (out << 1) + (cc ^ 48);
            cc = getchar();
        }
        return flag ? -out : out;
    } 
    
    inline void write(int x, char ch) {
        if (x < 0) putchar('-'), x = -x;
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 1e5 + 10;
    
    int n, m, fa[N], tr[N][2], dfn[N], id[N], times, f[N][17], sze[N], dep[N];
    
    int head[N], to[N << 1], nxt[N << 1], cnt;
    
    void add_edge(int u, int v) {
        to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
    }
    
    void dfs(int u, int las) {
        f[u][0] = fa[u] = las, dfn[u] = ++times, id[times] = u, sze[u] = 1, dep[u] = dep[las] + 1;
        for (int i = 1; i <= 16; i++) f[u][i] = f[f[u][i - 1]][i - 1];
        for (int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            if (v == las) continue;
            dfs(v, u), sze[u] += sze[v];
        }
    }
    
    int lca(int x, int y) {
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 16; i >= 0; i--) if (dep[f[x][i]] >= dep[y]) x = f[x][i];
        if (x == y) return x;
        for (int i = 16; i >= 0; i--) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    
    int Max[N << 2], inc[N << 2];
    
    void build(int u, int l, int r) {
        if (l == r) Max[u] = dep[id[l]]; // dep的下标是点的编号 
        else {
            int mid = (l + r) >> 1;
            build(lson), build(rson);
            Max[u] = max(Max[u << 1], Max[u << 1 | 1]);
        }
    }
    
    void pushdown(int u) {
        Max[u << 1] += inc[u], Max[u << 1 | 1] += inc[u];
        inc[u << 1] += inc[u], inc[u << 1 | 1] += inc[u];
        inc[u] = 0;
    }
    
    void modify(int u, int l, int r, int Left, int Right, int delta) {
        if (Left <= l && r <= Right) Max[u] += delta, inc[u] += delta;
        else {
            pushdown(u);
            int mid = (l + r) >> 1;
            if (Left <= mid) modify(lson, Left, Right, delta);
            if (mid < Right) modify(rson, Left, Right, delta);
            Max[u] = max(Max[u << 1], Max[u << 1 | 1]);
        } 
    }
    
    int query(int u, int l, int r, int Left, int Right) {
        if (Left <= l && r <= Right) return Max[u];
        pushdown(u);
        int mid = (l + r) >> 1, ans = 0;
        if (Left <= mid) ans = max(ans, query(lson, Left, Right));
        if (mid < Right) ans = max(ans, query(rson, Left, Right));
        return ans;
    }
    
    int query(int u, int l, int r, int pos) {
        if (l == r) return Max[u];
        pushdown(u);
        int mid = (l + r) >> 1;
        if (pos <= mid) return query(lson, pos);
        else return query(rson, pos);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
    }
    
    void splay(int x) {
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    int findrt(int x) {
        while (tr[x][0]) x = tr[x][0];
        return x;
    }
    
    void add(int u, int delta) {
        if (u) {
            u = findrt(u); // 注意LCT上的父子关系不代表原树上的父子关系 
            modify(1, 1, n, dfn[u], dfn[u] + sze[u] - 1, delta);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), add(tr[x][1], 1), add(tr[x][1] = y, -1);
    }
    
    int ask(int u) {
        return query(1, 1, n, dfn[u], dfn[u] + sze[u] - 1);
    }
    
    int main() {
        n = read(), m = read();
        for (int i = 1; i < n; i++) {
            int u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
        }
        dfs(1, 0), build(1, 1, n);
        for (int i = 1; i <= m; i++) {
            int op = read();
            if (op == 1) access(read());
            else if (op == 2) {
                int x = read(), y = read(), Lca = lca(x, y);
                x = query(1, 1, n, dfn[x]), y = query(1, 1, n, dfn[y]), Lca = query(1, 1, n, dfn[Lca]);
                // 注意单点查也要考虑到dfn[x]才是线段树上的位置 
                write(x + y - 2 * Lca + 1, '
    ');
            }
            else write(ask(read()), '
    ');
        }
        return 0;
    }

     

    [Ynoi2017] 由乃的 OJ

    调了一年终于过了,各种傻逼错误,我好菜哇

    可以并行计算$k$位

    设$f_0$为一开始全为$0$,最后会变成什么

      $f_1$为一开始全为$1$,最后会变成什么

    因为这k位间互不影响,所以每组询问最后再$Theta(k)$计算答案

    但注意维护时需要翻转链

    所以需要同时维护每条实链上从浅到深的信息和从深到浅的信息

    $reverse$时记得要交换

    $Theta(n+q (k+log n))$

    #include <bits/stdc++.h>
    
    #define check(x) (tr[fa[x]][1] == x)
    #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x)
    
    using namespace std;
    
    typedef unsigned long long ull;
    
    ull read() {
        ull out = 0;
        register char cc = getchar();
        while (cc < '0' || cc > '9') cc = getchar();
        while (cc >= '0' && cc <= '9') out = (out << 3) + (out << 1) + (cc ^ 48), cc = getchar();
        return out;
    }
    
    void write(ull x, char ch) {
        if (x == 0) putchar('0');
        else {
            int num = 0; char cc[22];
            while (x) cc[++num] = x % 10 + 48, x /= 10;
            while (num) putchar(cc[num--]);
        }
        putchar(ch);
    }
    
    const int N = 1e5 + 10;
    
    int head[N], to[N << 1], nxt[N << 1], cnt;
    
    void add_edge(int u, int v) {
        to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
    }
    
    int n, m, k, opt[N], fa[N], tr[N][2]; 
    
    ull a, b, l0[N], l1[N], r0[N], r1[N], val[N]; bool rev[N];
    
    void pushup(int x) {
        if (opt[x] == 1) l0[x] = r0[x] = 0, l1[x] = r1[x] = val[x];
        else if (opt[x] == 2) l0[x] = r0[x] = val[x], l1[x] = r1[x] = (~0ull);
        else if (opt[x] == 3) l0[x] = r0[x] = val[x], l1[x] = r1[x] = (~val[x]);
        int ls = tr[x][0], rs = tr[x][1];
        if (ls) {
            ull p = l0[x], q = l1[x]; // 因为分两步合并,所以合并时要先把原来值储存下来 
            l0[x] = (l0[ls] & q) | ((~l0[ls]) & p);
            l1[x] = (l1[ls] & q) | ((~l1[ls]) & p);
            p = r0[x], q = r1[x];
            r0[x] = (p & r1[ls]) | ((~p) & r0[ls]);
            r1[x] = (q & r1[ls]) | ((~q) & r0[ls]);
        }
        if (rs) {
            ull p = l0[x], q = l1[x];
            l0[x] = (p & l1[rs]) | ((~p) & l0[rs]);
            l1[x] = (q & l1[rs]) | ((~q) & l0[rs]);
            p = r0[x], q = r1[x];
            r0[x] = (r0[rs] & q) | ((~r0[rs]) & p);
            r1[x] = (r1[rs] & q) | ((~r1[rs]) & p);
        }
        
    }
    
    void reverse(int x) {
        swap(tr[x][0], tr[x][1]), swap(l0[x], r0[x]), swap(l1[x], r1[x]), rev[x] ^= 1;
    }
    
    void pushdown(int x) {
        if (rev[x]) {
            if (tr[x][0]) reverse(tr[x][0]);
            if (tr[x][1]) reverse(tr[x][1]);
            rev[x] = false;
        }
    }
    
    void pushall(int x) {
        if (!isroot(x)) pushall(fa[x]);
        pushdown(x);
    }
    
    void rotate(int x) {
        int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y);
        if (!isroot(y)) tr[z][opt2] = x; fa[x] = z;
        tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y;
        tr[x][opt1 ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    
    void splay(int x) {
        pushall(x);
        while (!isroot(x)) {
            int y = fa[x];
            if (!isroot(y)) rotate(check(x) == check(y) ? y : x);
            rotate(x);
        }
    }
    
    void access(int x) {
        for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x);
    }
    
    void makert(int x) {
        access(x), splay(x), reverse(x);
    }
    
    void split(int x, int y) {
        makert(x), access(y), splay(y);
    }
    
    void dfs(int u, int las) {
        fa[u] = las;
        for (int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            if (v == las) continue;
            dfs(v, u);
        }
        pushup(u); 
    }
    
    int main() {
        n = read(), m = read(), k = read();
        for (int i = 0; i < k; i++) b |= (1ull << i);
        for (int i = 1; i <= n; i++) opt[i] = read(), val[i] = read();
        for (int i = 1; i < n; i++) {
            int u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
        }
        dfs(1, 0);
        for ( ; m; m--) {
            int op = read(), x = read(), y = read(); ull z = read(); //注意z类型为ull 
            if (op == 1) {
                split(x, y);
                ull ans = 0;
                for (int i = k - 1; i >= 0; i--) {
                    if (l0[y] & (1ull << i)) ans |= (1ull << i);
                    else if ((1ull << i) <= z && (l1[y] & (1ull << i))) ans |= (1ull << i), z -= (1ull << i);
                }
                write(max(ans, l0[y]), '
    ');
            }
            else opt[x] = y, val[x] = z, splay(x);
        }
        return 0;
    }

     

    「ZJOI2016」大森林

    「ZJOI2018」历史

     

  • 相关阅读:
    Excel宏开发之合并单元格
    excel破解工作簿与工作表保护
    jquery高级编程学习
    Git 和 SVN 存储方式的差异对比
    SSH 连接时间超时
    linux 使用 Python 画图工具matplotlib 提示display 错误
    centos安装字体
    linux编译安装中configure、make和make install各自的作用
    npm run build
    centos ssh 免密码登录
  • 原文地址:https://www.cnblogs.com/Urushibara-Ruka/p/14472246.html
Copyright © 2011-2022 走看看