Nauuo and ODT(LCT维护连通块)

题面
思路简单
颜 色 数 ∗ 路 径 数 − ∑ c 是 一 种 颜 色 不 包 含 c 的 路 径 数 颜色数 * 路径数 - \sum_{c是一种颜色} 不包含c的路径数 cc
所以我们考虑颜色 c c c
我们把所有不是颜色 c c c的点都看作黑色,否则是白色。
所以不包含 c c c的路径数是黑色连通块的大小平方和。
考虑我们用LCT维护黑色连通块。
发现当一个点的颜色改变时会有 O ( 出 度 ) O(出度) O()的连边改变。
况且这个题不能通过把树3度化解决问题。
所以需要一个技巧就是:
每个黑点往其父亲连边即可。(这里的父亲是预处理定一个根dfs求出除了根以外所有点的父亲。)
那么每个联通块的最上面的点必定是白色点(需要保证定的根永远不会变成黑色)
那么就可以每次连/删 O ( 1 ) O(1) O(1)条边了。
然后考虑颜色改变。
白变黑先连边,其他的维护一个虚子树 s i z e 2 size^2 size2之和即可用 a c c e s s access access s p l a y splay splay后维护答案增量。

对于多个颜色,离线下来,跑多次即可。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 400005
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

/*

维护所有黑色联通块大小的平方
每个黑点向父亲连边。
LCT 
每个点维护虚子树大小平方和。
将一个点由白变黑:
access + splay解决一切问题 

*/

int n,m;
int info[maxn],Prev[maxn<<1],to[maxn<<1],c[maxn],cnt_e;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
vector<pair<int,int> >G[maxn];
bool col[maxn];
LL ans[maxn];

//LCT
int fa[maxn],ch[maxn][2],xsz[maxn],sz[maxn],ft[maxn];
LL sz2[maxn];
#define pa fa[x]
int isr(int x){ return ch[pa][0]^x && ch[pa][1]^x; }
int inr(int x){ return ch[pa][1] == x; }
void upd(int x){ sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + xsz[x]; }
void rot(int x){
	int y = fa[x] , z = fa[y] , c = inr(x);
	if(!isr(y)) ch[z][inr(y)] = x;
	(ch[y][c] = ch[x][!c]) && (fa[ch[y][c]] = y);
	fa[fa[ch[x][!c] = y] = x] = z;
	upd(y);
}
void splay(int x){
	for(;!isr(x);rot(x))
		if(!isr(pa)) rot(inr(pa) ^ inr(x) ? x : pa); 
	upd(x);
}
LL sqr(int a){return 1ll * a * a; }
int access(int x){
	int y=0;
	for(;x;x=fa[y=x]){
		splay(x);
		sz2[x] += sqr(sz[ch[x][1]]) - sqr(sz[y]);
		xsz[x] += sz[ch[x][1]] - sz[y];
		ch[x][1] = y;
		upd(x);
	}
}
void dfs0(int u,int ff){
	ft[u] = fa[u] = ff;
	if(u <= n) xsz[u] ++;
	for(int i=info[u],v;i;i=Prev[i]) if((v=to[i])^ff)
		dfs0(v,u);
	upd(u);
	if(ff){
		xsz[ff] += sz[u];
		sz2[ff] += sqr(sz[u]);
	}
}
int Sert(int x){
	access(x),splay(x);
	for(;ch[x][0];x=ch[x][0]);
	return x;
}


int main(){
	scanf("%d%d",&n,&m);
	ans[0] = 0;//1ll * n * n * n;
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);c[i]=x;
		G[x].push_back(make_pair(0,i));
	}
	for(int i=1;i<n;i++){
		int u,v;scanf("%d%d",&u,&v);
		Node(u,v),Node(v,u);
	}
	for(int i=1;i<=m;i++){
		int u,x;
		scanf("%d%d",&u,&x);
		G[c[u]].push_back(make_pair(i,u));
		G[c[u]=x].push_back(make_pair(i,u));
	}
	Node(n+1,1);
	dfs0(n+1,0);
	col[n+1] = 1;
	rep(i,1,n){
		rep(j,0,G[i].size()-1){
			int u = G[i][j].second;
			if(col[u]){
				col[u] = 0;
				splay(u);
				if(ft[u]){
					fa[u] = ft[u];
					access(ft[u]),splay(ft[u]);
					sz2[fa[u]] += sqr(sz[u]);
					xsz[fa[u]] += sz[u];
				}
				int t = Sert(u);
				xsz[u] ++ , upd(u);
				splay(t);
				int S = sz[t] - sz[ch[t][1]];
				access(u),splay(u);
				ans[G[i][j].first] -= sqr(sz[u] - S)
				 - sqr(sz[ch[u][0]] - S) - sqr(sz[ch[u][1]]) - sz2[u];
				
			}
			else{
				col[u] = 1;
				int t = Sert(u);
				splay(t);
				int S = sz[t] - sz[ch[t][1]];
				splay(u);
				ans[G[i][j].first] -= sz2[u] + sqr(sz[ch[u][0]] - S) + sqr(sz[ch[u][1]]) - sqr(sz[u] - S);
				if(ch[u][0]){
					fa[ch[u][0]] = 0;
					ch[u][0] = 0;
					upd(u);
				}
				xsz[u] --  , upd(u); 
			}
		}
		rep(j,0,G[i].size()-1){
			int u =G[i][j].second;
			if(col[u]){
					col[u] = 0;
				splay(u);
				if(ft[u]){
					fa[u] = ft[u];
					access(ft[u]),splay(ft[u]);
					sz2[fa[u]] += sqr(sz[u]);
					xsz[fa[u]] += sz[u];
				}
				access(u) , splay(u);
				xsz[u] ++ , upd(u);
			}
		}
	}
	for(int i=1;i<=m;i++) ans[i] += ans[i-1];
	for(int i=0;i<=m;i++) printf("%lld\n",ans[i]);
} 

你可能感兴趣的:(数据结构,LCT)