这题也是bzoj的题,题号似乎是bzoj4530,但是我没有权限号,所以没有在bzoj上提交,所以不知道能不能过。
题解:
LCT维护子树信息。
这篇顺便就当我的LCT维护子树信息的学习笔记好了。
我们知道,树剖的比较容易维护子树信息的,但是我之前一直以为LCT是不可以维护子树信息的,因为根据LCT的性质,它会把子树变为实子树(实边连接的)和虚子树(虚边连接的),所以我以为只能维护链上信息,觉得子树信息不可维护。学习之后发现,之前很多人说的树剖能做的LCT都能做暂时又变成了对的。。。
方法如下:
我们用数组s表示实子树+虚子树信息,si表示虚子树上维护的信息。我们考虑LCT的每一个操作对这两个需要维护的量的影响。
我们发现,会影响si的只有link操作和access操作。下面我们分别来分析一下。
对于link(x,y)操作,我们让x接到y上,相当于y多了一个虚儿子,所以用s[x]的信息更新si[y],然后pushup(y)来更新一下s[y]。当然,在这之前,我们makeroot(x)之后需要先access(y)+splay(y),据说是因为y不一定是原splay的根,不这样写的话原来的splay中y的祖先都没法被更新到。
对于access操作,我们在把它与根的路径连通时,si的变化的减去原来右儿子的s,加上新的右儿子的s。
然后就没什么难度了,查询其实就相当于是找x虚子树大小加上它自己与y的虚子树加上y本身的乘积,其实好像(y是根)s[x]*(si[y]+1)也是对的(注意开long long)。
最后,据说维护最值要对每个节点维护一个splay,我暂时不会。
下面是代码:
#include <bits/stdc++.h> using namespace std; int q,n,f[100010],c[100010][2],rev[100010],st[100010],s[100010],si[100010]; //s为实子树与虚子树总和,si为虚子树总和 int nroot(int x) { return c[f[x]][0]==x||c[f[x]][1]==x; } void pushup(int x) { s[x]=s[c[x][0]]+s[c[x][1]]+1+si[x]; } void pushdown(int x) { if(rev[x]) { swap(c[x][0],c[x][1]); rev[c[x][0]]^=1; rev[c[x][1]]^=1; rev[x]=0; } } void rotate(int x) { int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k]; if(nroot(y)) c[z][c[z][1]==y]=x; c[x][!k]=y; c[y][k]=w; if(w) f[w]=y; f[y]=x; f[x]=z; pushup(y); pushup(x); } void splay(int x) { int y=x,z=0; st[++z]=y; while(nroot(y)) { y=f[y]; st[++z]=y; } while(z) pushdown(st[z--]); while(nroot(x)) { y=f[x],z=f[y]; if(nroot(y)) { if(c[z][0]==y ^ c[y][0]==x) rotate(y); else rotate(x); } rotate(x); } pushup(x); } void access(int x) { int y=0; while(x) { splay(x); si[x]+=s[c[x][1]]; si[x]-=s[y]; c[x][1]=y; y=x; x=f[x]; //不用pushup,因为access对总和s是没用影响的 } } void makeroot(int x) { access(x); splay(x); rev[x]^=1; } void link(int x,int y) { makeroot(x); access(y); splay(y); f[x]=y; si[y]+=s[x]; pushup(y); } int main() { scanf("%d%d",&n,&q); char opt; int x,y; for(int i=1;i<=n;++i) s[i]=1; for(int i=1;i<=q;++i) { opt=getchar(); if(opt!='A'&&opt!='Q') opt=getchar(); scanf("%d%d",&x,&y); if(opt=='A') link(x,y); else { makeroot(x); access(y); splay(y); printf("%lld\n",(long long)(si[x]+1)*(si[y]+1)); } } return 0; }