这道题目太神啦!
我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去。那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器——启发式合并,那么我们就可以在均摊O(log2n)的时间内合并一颗树,这题就可以完美的AC啦!
什么,你问怎么维护重心?我们可以记录一个值sb表示子树的大小。怎么维护sb呢?我们可以采用打标记的方法,把新加入的节点到根的路径上的点的sb值都+1
对于维护答案,我们维护一个sm变量,来保存子树内所有节点到这个节点的距离之和,在更新的时候采用维护一个等差数列的方法,记录首项和公差,然后在pushdown的时候如果走给Splay的左儿子,那么还要加上这个点的右儿子到首项上(因为右儿子实际上是左儿子的后代(LCT意义下))。

#include <cstdio>
#include <algorithm>
#include <assert.h>
using namespace std;
#define MAXN 40005
#define lc(x) (t[x].s[0])
#define rc(x) (t[x].s[1])
int n, m, adj[MAXN], c, ans;
inline void GET(int &n) {
    char c; n = 0;
    do c = getchar(); while(c > '9' || c < '0');
    while(c >= '0' && c <= '9') {n=n*10+c-'0';c=getchar();}
}
struct Node { int v, nxt; } e[MAXN << 1];
inline void add(int u, int v) {
    ++ c; e[c].v = v; e[c].nxt = adj[u]; adj[u] = c;
}
struct Link_Cut_Cactus {
    int fa[MAXN], sta[MAXN];
    struct Spaly { int f, sz, a, d, sm, s[2], sb, db; } t[MAXN];
    /**依次表示爸爸,spaly字数大小,首项,公差,子树到此节点的距离,儿子,子树大小**/
    inline void init() { for(int i = 1; i <= n; ++ i) t[i].sz = t[i].sb = 1; }
    inline void pushup(int x) { t[x].sz = t[lc(x)].sz + t[rc(x)].sz + 1; }
    inline void add1(int x, int tag) {
        if(x) { t[x].sb += tag; t[x].db += tag; }
    }
    inline void add2(int x, int a, int d) {
        if(x) { t[x].sm += a + t[rc(x)].sz * d; t[x].a += a; t[x].d += d; }
    }
    inline void pushdown(int x) {
        if(t[x].db) { add1(lc(x), t[x].db); add1(rc(x), t[x].db); t[x].db = 0; }
        if(t[x].d) { add2(lc(x), t[x].a + (t[rc(x)].sz+1)*t[x].d, t[x].d); add2(rc(x), t[x].a, t[x].d); t[x].d = 0; }
    }
    inline void rot(int x) {
        int y = t[x].f, z = t[y].f;
        bool f = rc(y) == x;
        fa[x] = fa[y]; t[x].f = z;
        t[y].s[f] = t[x].s[f^1];
        t[x].s[f^1] = y; t[y].f = x;
        if(t[y].s[f]) t[t[y].s[f]].f = y;
        if(z) t[z].s[ rc(z) == y ] = x;
        pushup(y);
    }
    inline void Splay(int x) {
        int tp = 0;
        for(int p = x; p; p = t[p].f) sta[++ tp] = p;
        while(tp) pushdown(sta[tp --]);
        for(int y, z; (y = t[x].f); rot(x)) {
            z = t[y].f; if(!z) continue;
            if((rc(z) == y) == (rc(y) == x)) rot(y);
            else rot(x);
        }
        pushup(x);
    }
    inline void expose(int x, int y = 0) {
        Splay(x);
        if(t[x].s[1]) {
            t[t[x].s[1]].f = 0;
            fa[t[x].s[1]] = x;
        }
        t[x].s[1] = y;
        if(y) t[y].f = x;
        pushup(x);
    }
    inline void access(int x, int y = 0) {
        while(x) { expose(x, y); y = x; x = fa[x]; }
    }
    inline int root(int x) {
        access(x); Splay(x); while(lc(x)) x = lc(x); return Splay(x), x;
    }
    inline void addleaf(int x, int to) {
        fa[x] = to; t[x].sz = 1; t[x].a = lc(x) = rc(x) = 0;
        t[x].d = t[x].db = t[x].f = t[x].sb = t[x].sm = 0;
        to = root(to); access(x); Splay(to); add1(to, 1); add2(to, 0, 1);
        for(x = rc(to); lc(x); x = lc(x)); Splay(x);
        int vx = t[to].sb, vy = t[x].sb;
        if(vy * 2 > vx) {
            t[x].sb = vx; t[to].sb -= vy;
            t[to].sm -= t[x].sm + vy;
            t[x].sm += t[to].sm + vx - vy;
            access(x); Splay(to);
            swap(lc(to), rc(to));
        }
    }
    void dfs(int u, int fa) {
        addleaf(u, fa);
        for(int i = adj[u]; i; i = e[i].nxt)
            if(e[i].v != fa) dfs(e[i].v, u);
    }
    void Link(int u, int to) {
        int x = root(u), y = root(to);
        ans -= t[x].sm + t[y].sm;
        if(t[x].sb < t[y].sb) swap(u, to);
        dfs(to, u); add(u, to); add(to, u);
        ans += t[root(u)].sm;
    }
} lct;
int main() {
    char op[5]; int u, v;
    scanf("%d%d", &n, &m);
    lct.init();
    for(int i = 1; i <= m; ++ i) {
        scanf("%s", op);
        if(op[0] == 'A') {
            GET(u); GET(v); lct.Link(u, v);
        }
        else printf("%d\n", ans);
    }
    return 0;
}
posted on 2016-03-10 00:26  geng4512  阅读(508)  评论(0编辑  收藏  举报