[BZOJ4530][Bjoi2014]大融合 LCT + 启发式合并
[BZOJ4530][Bjoi2014]大融合
试题描述
小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够
联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因
为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
询问。
输入
第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
1≤N,Q≤100000
输出
对每个查询操作,输出被查询的边的负载。
输入示例
8 6 A 2 3 A 3 4 A 3 8 A 8 7 A 6 5 Q 3 8
输出示例
6
题解
LCT + 启发式合并
我不敢直接用LCT直接维护有根树的子树size值,所以每次合并用启发式合并暴力将较小的连通块dfs重构(其实就是换根),再插到另一个连通块中。
当树Tree1的树根要作为另一棵树Tree2中节点u的儿子时,需要将Tree2中节点u到根节点的路径上每个节点的权值加上Tree1的大小,这是一个链上的问题,可以用LCT解决。(代码后附有更强的样例)
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <cstdlib> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 100010 #define maxm 200010 #define LL long long int n, q, m, head[maxn], nxt[maxm], to[maxm]; void AddEdge(int a, int b) { to[++m] = b; nxt[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; nxt[m] = head[a]; head[a] = m; return ; } int pa[maxn], siz[maxn]; int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); } int fa[maxn], ch[maxn][2], val[maxn], addv[maxn]; bool isroot(int u) { return ch[fa[u]][0] != u && ch[fa[u]][1] != u; } void pushdown(int u) { int l = ch[u][0], r = ch[u][1]; if(addv[u]) { addv[l] += addv[u]; addv[r] += addv[u]; val[l] += addv[u]; val[r] += addv[u]; addv[u] = 0; } return ; } void maintain(int u) { return ; } void rotate(int u) { int y = fa[u], z = fa[y], l = 0, r = 1; if(ch[y][1] == u) swap(l, r); if(!isroot(y)) ch[z][ch[z][1]==y] = u; fa[u] = z; fa[y] = u; fa[ch[u][r]] = y; ch[y][l] = ch[u][r]; ch[u][r] = y; maintain(y); maintain(u); return ; } int S[maxn], top; void splay(int u) { S[++top] = u; for(int t = u; !isroot(t); t = fa[t]) S[++top] = fa[t]; while(top) pushdown(S[top--]); while(!isroot(u)) { int y = fa[u], z = fa[y]; if(!isroot(y)) { if((ch[y][0] == u) ^ (ch[z][0] == u)) rotate(u); else rotate(y); } rotate(u); } return ; } void access(int u) { for(int t = 0; u; u = fa[u]) { splay(u); ch[u][1] = t; maintain(u); t = u; } return ; } void add(int u, int v) { access(u); splay(u); addv[u] += v; val[u] += v; return ; } int query(int u) { access(u); splay(u); return val[u]; } void rebuild(int u) { ch[u][0] = ch[u][1] = 0; val[u] = 1; for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) { fa[to[e]] = u; rebuild(to[e]); val[u] += val[to[e]]; } return ; } int main() { n = read(); q = read(); for(int i = 1; i <= n; i++) pa[i] = i, val[i] = siz[i] = 1; while(q--) { char tc = Getchar(); while(!isalpha(tc)) tc = Getchar(); int u = read(), v = read(); if(tc == 'A') { int a = findset(u), b = findset(v); if(siz[a] > siz[b]) swap(a, b), swap(u, v); pa[a] = b; siz[b] += siz[a]; fa[u] = v; AddEdge(u, v); rebuild(u); // printf("siz[%d] val[%d]: %d %d\n", b, u, siz[b], val[u]); add(v, siz[a]); } if(tc == 'Q') { LL x = (LL)min(query(u), query(v)); // printf("%d %d\n", query(u), query(v)); printf("%lld\n", x * (siz[findset(u)] - x)); } } return 0; } /* in: 8 14 A 2 3 Q 2 3 A 3 4 Q 2 3 A 3 8 Q 3 8 A 8 7 Q 3 4 A 6 5 Q 5 6 Q 3 8 A 1 6 A 1 8 Q 1 8 out: 1 2 3 4 1 6 15 */