LOJ558 我们的 CPU 遭到攻击

我们的 CPU 遭到攻击

给你一个有 \(n\) 个点的森林,点有黑白两种颜色,初始时所有点都是白色,森林的每条边有边权,初始时这个森林有 \(m\) 条边。

对这个森林进行 \(k\) 次操作,操作有三种:

  • L u v w:添加一条连接 \(u\)\(v\),长度为 \(w\) 的边。

  • C u v:删除连接 \(u\)\(v\) 的边(保证存在)。

  • F u:反转点 \(u\) 的颜色(黑变白,白变黑)。

  • Q u:询问所有与 \(u\) 相连的黑点到 \(u\) 的距离之和。(相连指的是在同一连通块中

\(0\le k< n\le 10^5\)\(m\le 3\times 10^5\)\(|w|\le 10^7\),保证任何时刻这个图均为森林(即不会出现环)。

题解

https://jklover.hs-blog.cf/2019/12/27/LCT-题目选做/

LCT 维护子树信息.

把边拆成点,变成维护路径点权之和.

由于 LCT 支持换根,所以只需要分别维护出一棵 Splay 中所有点到深度最小,最大的点的距离和,翻转的时候交换.

为了能合并 Splay 左右儿子的信息,还需要维护出子树内黑点的数目,虚边的子树内黑点到对应子树根的距离和.

当虚边被更改时将对应信息更新就可以了,时间复杂度 \(O(n\log n)\).

CO int N=5e5+10;
int ch[N][2],fa[N],rev[N];
int col[N],si[N],siz[N]; // size of imaginary subtree
int val[N];int64 sumv[N]; // sum of weight of edge
int64 sumi[N],suml[N],sumr[N]; // sum of distance of imaginary subtree

IN bool nroot(int x){
	return ch[fa[x]][0]==x or ch[fa[x]][1]==x;
}
IN void push_up(int x){
	siz[x]=col[x]+si[x]+siz[ch[x][0]]+siz[ch[x][1]];
	sumv[x]=val[x]+sumv[ch[x][0]]+sumv[ch[x][1]];
	suml[x]=sumi[x]+suml[ch[x][0]]+suml[ch[x][1]]+(col[x]+si[x]+siz[ch[x][1]])*(val[x]+sumv[ch[x][0]]);
	sumr[x]=sumi[x]+sumr[ch[x][0]]+sumr[ch[x][1]]+(col[x]+si[x]+siz[ch[x][0]])*(val[x]+sumv[ch[x][1]]);
}
IN void reverse(int x){
	swap(ch[x][0],ch[x][1]);
	swap(suml[x],sumr[x]);
	rev[x]^=1;
}
IN void push_down(int x){
	if(rev[x]){
		if(ch[x][0]) reverse(ch[x][0]);
		if(ch[x][1]) reverse(ch[x][1]);
		rev[x]=0;
	}
}
IN void rotate(int x){
	int y=fa[x],z=fa[y],l=x==ch[y][1],r=l^1;
	if(nroot(y)) ch[z][y==ch[z][1]]=x;fa[x]=z;
	ch[y][l]=ch[x][r],fa[ch[x][r]]=y;
	ch[x][r]=y,fa[y]=x;
	push_up(y);
}
void splay(int x){
	vector<int> stk(1,x);
	for(int i=x;nroot(i);) stk.push_back(i=fa[i]);
	for(;stk.size();stk.pop_back()) push_down(stk.back());
	for(;nroot(x);rotate(x)){
		int y=fa[x],z=fa[y];
		if(nroot(y)) rotate((x==ch[y][1])!=(y==ch[z][1])?x:y);
	}
	push_up(x);
}
void access(int x){
	for(int y=0;x;y=x,x=fa[x]){
		splay(x);
		si[x]+=siz[ch[x][1]];
		sumi[x]+=suml[ch[x][1]];
		ch[x][1]=y;
		si[x]-=siz[ch[x][1]];
		sumi[x]-=suml[ch[x][1]];
		push_up(x);
	}
}
IN void make_root(int x){
	access(x),splay(x),reverse(x);
}
int find_root(int x){
	access(x),splay(x);
	for(;ch[x][0];x=ch[x][0]);
	splay(x);
	return x;
}
IN void split(int x,int y){
	make_root(x),access(y),splay(y);
}
IN void link(int x,int y){
	make_root(x),access(y),splay(y);
	fa[x]=y;
	si[y]+=siz[x];
	sumi[y]+=suml[x];
	push_up(y);
}
IN void cut(int x,int y){
	split(x,y);
	fa[x]=0;
	ch[y][0]=0;
	push_up(y);
}

int main(){
	int n=read<int>(),m=read<int>(),q=read<int>();
	for(int i=1;i<=m;++i){
		int x=read<int>(),y=read<int>(),w=read<int>();
		val[++n]=w;
		push_up(n);
		link(x,n),link(y,n);
	}
	for(int i=1;i<=q;++i){
		char opt[2];scanf("%s",opt);
		if(opt[0]=='L'){
			int x=read<int>(),y=read<int>(),w=read<int>();
			val[++n]=w;
			push_up(n);
			link(x,n),link(y,n);
		}
		else if(opt[0]=='C'){
			int x=read<int>(),y=read<int>();
			split(x,y);
			int p=ch[y][0];
			cut(p,x),cut(p,y);
		}
		else if(opt[0]=='F'){
			int x=read<int>();
			access(x),splay(x);
			col[x]^=1;
			push_up(x);
		}
		else{
			int x=read<int>();
			make_root(x);
			printf("%lld\n",suml[x]);
		}
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/13174075.html