我们的 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 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(),m=read(),q=read();
for(int i=1;i<=m;++i){
int x=read(),y=read(),w=read();
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(),y=read(),w=read();
val[++n]=w;
push_up(n);
link(x,n),link(y,n);
}
else if(opt[0]=='C'){
int x=read(),y=read();
split(x,y);
int p=ch[y][0];
cut(p,x),cut(p,y);
}
else if(opt[0]=='F'){
int x=read();
access(x),splay(x);
col[x]^=1;
push_up(x);
}
else{
int x=read();
make_root(x);
printf("%lld\n",suml[x]);
}
}
return 0;
}