动态维护森林
显然考虑 $LCT$
但是发现询问求的是子树大小,比较不好搞
维护 $sum[x]$ 表示节点 $x$ 的子树大小,$si[x]$ 表示 $x$ 的子树中虚儿子的子树大小和
那么 $pushup$ 可以这样写:
inline void pushup(int x) { sum[x]=sum[c[x][0]]+sum[c[x][1]]+si[x]+1; }
考虑什么时候 $si$ 会变
首先对于 $rotate,splay$ 因为都是对一条实链搞,所以对虚边没影响
然后考虑 $access$ ,发现边的虚实有改变
原本 $x$ 的右儿子变成另一个节点,那么要记得更新
然后 $makeroot$ ,发现我们翻转的是一条实链,所以同样不会对虚边产生影响
然后 $split$ ,调用了 $makeroot,access,splay$ 这些之前都考虑过了
然后 $link$,发现多了一条虚边,所以要记得更新一下
然后 $cut$,因为断的是实边,所以不会改变
那么询问时只要 $makeroot(x),access(y),splay(y)$ ,然后 $y$ 的右儿子就是 $x$ ,输出 $(si[x]+1)*(si[y]+1)$ 即可
具体看代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+7; int c[N][2],fa[N],si[N],sum[N]; bool rev[N];//维护的是延时标记 inline void pushup(int x) { sum[x]=sum[c[x][0]]+sum[c[x][1]]+si[x]+1; } inline void pushdown(int x) { if(!x||!rev[x]) return; int &lc=c[x][0],&rc=c[x][1]; swap(lc,rc); rev[x]=0; if(lc) rev[lc]^=1; if(rc) rev[rc]^=1; } inline void rever(int x) { rev[x]^=1; pushdown(x); } inline bool notroot(int x) { return (c[fa[x]][0]==x)|(c[fa[x]][1]==x); } inline void rotate(int x) { int y=fa[x],z=fa[y],d=(c[y][1]==x); if(notroot(y)) c[z][c[z][1]==y]=x; fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y; c[y][d]=c[x][d^1]; c[x][d^1]=y; pushup(y); pushup(x); } void push_tag(int x) { if(notroot(x)) push_tag(fa[x]); else pushdown(x); pushdown(c[x][0]); pushdown(c[x][1]); } inline void splay(int x) { push_tag(x); while(notroot(x)) { int y=fa[x],z=fa[y]; if(notroot(y)) { if(c[z][0]==y ^ c[y][0]==x) rotate(x); else rotate(y); } rotate(x); } } inline void access(int x) { for(int y=0;x;y=x,x=fa[x]) { splay(x); si[x]+=(sum[c[x][1]]-sum[y]);//记得更新si c[x][1]=y; pushup(x); } } inline void makeroot(int x) { access(x); splay(x); rever(x); } inline void split(int x,int y) { makeroot(x); access(y); splay(y); } inline void link(int x,int y) { split(x,y); fa[x]=y; si[y]+=sum[x];/*更新si*/ pushup(y); } inline void query(int x,int y) { split(x,y);//和makeroot(x),access(y),splay(y)是同样的写法 printf("%lld ",1ll*(si[x]+1)*(si[y]+1)); } int n,m; int main() { int a,b; char s[7]; n=read(); m=read(); for(int i=1;i<=n;i++) sum[i]=1; while(m--) { scanf("%s",s); a=read(),b=read(); if(s[0]=='A') link(a,b); else query(a,b); } return 0; }