时空限制 1000ms / 128MB
小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。 这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数量。
第一行包含两个整数 N, Q表示星球的数量和操作的数量。星球从 11 开始编号。
接下来的 Q 行,每行是如下两种格式之一:
A x y 表示在 x和 y 之间连一条边。保证之前 x 和 y是不联通的。
Q x y表示询问 (x,y) 这条边上的负载。保证 x 和 y 之间有一条边。
对每个查询操作,输出被查询的边的负载。
对于所有数据, 1 ≤ N , Q ≤ 1 0 5 1≤N,Q≤10^5 1≤N,Q≤105
题目中涉及动态加边的操作,所以马上想到LCT
但是LCT只擅长维护链信息,而此题要求维护子树,这就涉及到了LCT一个新的巧妙变化
先做两个定义
结合LCT的特性我们又知道以下几点
那么为了得知 x x x原树中子树的信息我们可以
这是因为 a c c e s s ( x ) access(x) access(x)后会使 x x x没有实儿子
那么为了为了维护虚实儿子的信息,一些涉及虚实儿子转换的LCT函数就需要修改
以此题中维护子树大小为例, T s [ x ] Ts[x] Ts[x]为 x x x虚儿子信息, s i z e [ x ] size[x] size[x]为原树信息总和
即 T s [ x ] Ts[x] Ts[x]只包含x所有虚儿子(通过轻边指向x)的信息总和
而 s i z e [ x ] size[x] size[x]实际上是 x x x在 L C T LCT LCT中的所有儿子的信息总和(包括辅助树Splay左右儿子的总和以及被轻边所指的虚儿子的总和)
那么要修改的函数应该有三个
首先 a c c e s s ( x ) access(x) access(x)肯定要改
void access(int x)
{
int t=0;
while(x)
{
splay(x);
Ts[x]+=size[ch[x][1]]-size[t];
//加上由实儿子变成虚儿子的size,减去虚儿子变成实儿子的size
ch[x][1]=t;
update(x);
t=x; x=fa[x];
}
}
l i n k ( x , y ) link(x,y) link(x,y)使得 x , y x,y x,y之间多了一条轻边,即 x x x变成了 y y y的虚儿子,所以 T s Ts Ts肯定要改
还有一处不同是 m k r t ( y ) mkrt(y) mkrt(y),因为连边后 y y y到其 s p l a y splay splay根路径上的结点信息都会改变,这么做省去更新的麻烦
void match(int x,int y)
{
mkrt(x); mkrt(y);
fa[x]=y;
Ts[y]+=size[x];
}
最后就是更新函数
void update(int x)
{
int lc=ch[x][0],rc=ch[x][1];
size[x]=size[lc]+size[rc]+Ts[x]+1;
//sum[x]=sum[lc]+size[rc]+Tsum[x]+val[x];//权值总和
}
还有一个 c u t ( x , y ) cut(x,y) cut(x,y)似乎也涉及断边?
事实上 c u t ( x , y ) cut(x,y) cut(x,y)断掉的是重边,也就是实儿子间断开,并不影响虚儿子信息更新
知道了这么多,这题其实也就挺裸了
查询也很简单
mkrt(u); access(v); splay(v);
printf("%lld\n",(Ts[u]+1ll)*(Ts[v]+1ll));
完整代码
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
const int maxn=500010;
int n,m;
int fa[maxn],ch[maxn][2];
int lzy[maxn],st[maxn],cnt;
lt Ts[maxn],size[maxn];
char ss[5];
int isrt(int x)
{
return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void push(int x)
{
if(!lzy[x]) return;
swap(ch[x][0],ch[x][1]);
lzy[ch[x][0]]^=1; lzy[ch[x][1]]^=1;
lzy[x]=0;
}
void update(int x)
{
int lc=ch[x][0],rc=ch[x][1];
size[x]=size[lc]+size[rc]+Ts[x]+1;
}
void rotate(int x)
{
int y=fa[x],z=fa[y];
int d=(ch[y][0]==x);
if(!isrt(y))
{
if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
}
fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
ch[y][d^1]=ch[x][d]; ch[x][d]=y;
update(y); update(x);
}
void splay(int x)
{
int top=0; st[++top]=x;
for(int i=x;!isrt(i);i=fa[i])
st[++top]=fa[i];
while(top) push(st[top--]);
while(!isrt(x))
{
int y=fa[x],z=fa[y];
if(!isrt(y))
{
if((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
int t=0;
while(x)
{
splay(x);
Ts[x]+=size[ch[x][1]]-size[t];
ch[x][1]=t;
update(x);
t=x; x=fa[x];
}
}
void mkrt(int x)
{
access(x); splay(x);
lzy[x]^=1;
}
void match(int x,int y)
{
mkrt(x); mkrt(y);
fa[x]=y;
Ts[y]+=size[x];
}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;++i) size[i]=1;
for(int i=1;i<=m;++i)
{
scanf("%s",&ss);
int u=read(),v=read();
if(ss[0]=='A') match(u,v);
else if(ss[0]=='Q')
{
mkrt(u); access(v); splay(v);
printf("%lld\n",(Ts[u]+1ll)*(Ts[v]+1ll));
}
}
return 0;
}