[BJOI2014]大融合

Portal

可以简单发现, 经过一条边的路径条数就是这条边左边的点数乘以右边的点数。

那么就只要动态维护一棵树两边的点的size就可以了。

那么就用(整体的点数-子树的点数)就是另外一部分的点数。

在LCT上,儿子有左儿子,右儿子和虚儿子之分。

那么我们在维护的时候就分类讨论一下就可以了。

注意到只有Linkaccess操作会改变虚儿子。 那么我们只要在这个时候维护虚儿子就可以了。


// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
    for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

template <int N> struct LCT {
    struct node {
        int fa, ch[2], revTag, sumSize, voidSonSize;
    }t[N + 1];
    int _top, stk[N + 1];

#define fa(x) (t[(x)].fa)
#define lc(x) (t[(x)].ch[0])
#define rc(x) (t[(x)].ch[1])

    bool isroot(int u) { 
        return lc(fa(u)) != u && rc(fa(u)) != u; 
    }
    void pushup(int u) { 
        t[u].sumSize = t[lc(u)].sumSize + t[rc(u)].sumSize + 1 + t[u].voidSonSize; 
    }
    void setRev(int u) {
        t[u].revTag ^= 1, swap(lc(u), rc(u));
    }
    void pushdown(int u) {
        if (t[u].revTag) {
            setRev(lc(u)); setRev(rc(u));
            t[u].revTag = 0;
        }
    }

    void rotate(int u) {
        int y = fa(u), z = fa(y), dir = rc(y) == u;
        if (!isroot(y)) t[z].ch[t[z].ch[1] == y] = u; t[u].fa = z;
        t[y].ch[dir] = t[u].ch[dir ^ 1]; t[t[u].ch[dir ^ 1]].fa = y;
        t[u].ch[dir ^ 1] = y; t[y].fa = u;
        pushup(y); pushup(u);
    }
    void splay(int u) {
        stk[_top = 1] = u;
        for (int y = u; !isroot(y); y = fa(y)) stk[++_top] = fa(y); 
        while (_top) pushdown(stk[_top--]);

        while (!isroot(u)) {
            int y = fa(u), z = fa(y);
            if (!isroot(y)) 
                (rc(z) == y) ^ (rc(y) == u) ? rotate(u) : rotate(y);
            rotate(u);
        }
        pushup(u);
    }

    void access(int u) {
        for (int y = 0; u; u = fa(y = u)) {
            splay(u);
            t[u].voidSonSize += t[rc(u)].sumSize - t[y].sumSize;
            t[u].ch[1] = y, pushup(u);
        }
    }
    void makeRoot(int u) {
        access(u), splay(u), setRev(u);
    }
    int findRoot(int u) {
        access(u); splay(u);
        while (lc(u)) pushdown(u), u = lc(u);
        return u;
    }
    void link(int u, int v) {
        makeRoot(u); makeRoot(v);
        if (findRoot(u) != v) t[v].fa = u, t[u].voidSonSize += t[v].sumSize, pushup(u);
    }
    void cut(int u, int v) {
        makeRoot(u); access(v); splay(v);
        if (findRoot(v) == u && t[v].ch[0] == u && fa(u) == v) {
            t[v].ch[0] = t[u].fa = 0;
            pushup(v);
        }
    }
#undef fa
#undef lc
#undef rc
};

const int Maxn = 100009;
LCT <Maxn> s;
int n, q;

void init() {
    n = read(); q = read();
}

void solve() {
    rep (i, 1, n) s.t[i].sumSize = 1;
    rep (i, 1, q) {
        char Cl2 = getchar();
        int H2SO4 = read(), NaCl = read();
        if (Cl2 == 'A') s.link(H2SO4, NaCl);
        if (Cl2 == 'Q') {
            s.makeRoot(H2SO4), s.makeRoot(NaCl);
            LL HCl = 1ll * s.t[H2SO4].sumSize * (1ll * s.t[NaCl].sumSize - s.t[H2SO4].sumSize);
            printf("%lld
", HCl);
        }
    }
}

int main() {
//	freopen("LG4219.in", "r", stdin);
//	freopen("LG4219.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("
Running time: %.3lf(s)
", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}
原文地址:https://www.cnblogs.com/qrsikno/p/10121074.html