BJOI2014 大融合

  1. 保证初始所有点都孤立
  2. 保证连的边的端点在连这条边前不联通

即整个图一直都是森林。

先连好所有的边,定好每棵树的根。

这样,每次询问就是在树上的一条边 (fa[x], x), 答案是边两边连通块 size 的乘积。

fa[x] 那边的大小不好办, x 这边的正好是一个子树型的询问, 连通块总的 size 可以简单搞, 故问题焦点是 x 这边的大小。

考虑连一条边 (v = fa[u], u) 对子树和的影响, 那就是 while(v) siz[v] += siz[u], v = fa[v] 这样的感觉。

考虑用并查集维护一个点能跳 fa 跳到的最上面的点, 对于链加+单点求值直接差分+树状数组即可 O(nlog2n), 建议不要像我一样有事没事写个树剖(。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 233;

int n, Q;
struct cmd{ int op,x,y; } comm[N];

int ecnt, hd[N], nt[N*2+1], vr[N*2+1];
void ad(int x, int y) {	nt[++ecnt] = hd[x], hd[x] = ecnt, vr[ecnt] = y;	}

int dep[N], siz[N], fa[N], son[N], tp[N];
int dfntot, in[N], out[N];
void dfs1(int x, int F, int D) {
	dep[x] = D, siz[x] = 1, fa[x] = F;
	for(int i=hd[x]; i; i=nt[i]) {
		int y = vr[i];
		if(y == F) continue;
		dfs1(y, x, D + 1);
		siz[x] += siz[y];
		if(siz[y] > siz[son[x]]) son[x] = y;
	}
}
void dfs2(int x, int T) {
	in[x] = ++dfntot;
	tp[x] = T;
	if(son[x]) dfs2(son[x], T);
	for(int i=hd[x]; i; i=nt[i]) {
		int y = vr[i];
		if(y == fa[x] || y == son[x]) continue;
		dfs2(y, y);
	}
	out[x] = dfntot;
}

struct dsu{
	int fa[N], siz[N];
	void init(int n) {
		for(int i=1; i<=n; ++i) siz[fa[i]=i] = 1;
	}
	int fid(int x) { return fa[x]==x ? x : fa[x]=fid(fa[x]);}
	void lef(int x, int y) { y = fid(y); siz[fa[x]=y] += siz[x]; }
	int sz(int x) {return siz[fid(x)];}
} S;

int t[N];
void ins(int x, int v) {
	for(;x<=n;x+=(x&(-x))) t[x]+=v;
}
int ask_(int x) {int res=0;
	for(;x;x-=(x&(-x))) res+=t[x];
	return res;
}
int ask(int l, int r) { return ask_(r) - ask_(l-1);}

int main()
{
	scanf("%d%d", &n, &Q);
	char op[5];
	for(int i=1; i<=Q; ++i) {
		scanf("%s%d%d", op, &comm[i].x, &comm[i].y);
		comm[i].op = (op[0] == 'A') ? 1 : 2;
		
		if(comm[i].op == 1) ad(comm[i].x, comm[i].y), ad(comm[i].y, comm[i].x);
	}
	for(int i=1; i<=n; ++i) if(!dep[i]) dfs1(i, 0, dep[i] = 1), dfs2(i, i);
	S.init(n);
//	for(int i=1; i<=n; ++i) ins(i, 1);
	for(int i=1; i<=Q; ++i) {
		int x = comm[i].x, y = comm[i].y;
		if(fa[x] != y) swap(x,y);
		// fa[x] == y
		if(comm[i].op == 1)
		{
			int Top = S.fid(y);
			int sizx = ask(in[x], out[x]) + 1;
			ins(in[y], sizx);
			if(fa[Top]) ins(in[fa[Top]], -sizx);
			S.lef(x, y);
		}
		else
		{
			int Siz = S.sz(y);
			int sizx = ask(in[x], out[x]) + 1;
			cout << 1ll * sizx * (Siz - sizx) << '\n';
		}
	}
	return 0;
}
上一篇:【CF629E】Famil Door and Roads


下一篇:点分治与点分树 学习笔记