这题也是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;
}