[lct] Luogu P4219 大融合
题目描述
小强要在NN个孤立的星球上建立起一套通信系统。这套通信系统就是连接NN个点的一个树。 这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了55条边。其中,(3,8)(3,8)这条边的负载是66,因 为有六条简单路径2-3-82−3−8,2-3-8-72−3−8−7,3-8,3-8-73−8,3−8−7,4-3-84−3−8,4-3-8-74−3−8−7路过了(3,8)(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的 询问。
输入输出格式
输入格式:
第一行包含两个整数 N, QN,Q,表示星球的数量和操作的数量。星球从 11 开始编号。
接下来的 QQ 行,每行是如下两种格式之一:
A x y
表示在 xx和 yy 之间连一条边。保证之前 xx 和 yy是不联通的。Q x y
表示询问 (x,y)(x,y) 这条边上的负载。保证 xx 和 yy 之间有一条边。
输出格式:
对每个查询操作,输出被查询的边的负载。
输入输出样例
说明
对于所有数据,1≤N,Q≤10^51≤N,Q≤105
题解
- 对于lct一般都是维护链上的操作,那么怎么维护子树上的信息呢
- 定义siz[x]表示x的所有虚儿子的子树大小和,size[x]表示x的所有虚儿子+实儿子+自己的子树大小和
- 那么只要在虚实边变化的时候维护一下siz的大小,同时维护size就好了
- 求x的子树大小,只要access(x),然后siz[x]+1就是答案了
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #define N 300010 5 using namespace std; 6 int n,m,f[N],ch[N][2],v[N],s[N],size[N],tag[N]; 7 int nroot(int x) { return ch[f[x]][0]==x||ch[f[x]][1]==x; } 8 void pushup(int x) { size[x]=size[ch[x][0]]+size[ch[x][1]]+s[x]+1; } 9 void pushdown(int x) { if (tag[x]) swap(ch[x][0],ch[x][1]),tag[ch[x][0]]^=1,tag[ch[x][1]]^=1,tag[x]=0; } 10 void work(int x) { if (nroot(x)) work(f[x]); pushdown(x); } 11 void rotate(int x) 12 { 13 int y=f[x],z=f[y],k=ch[y][1]==x,w=ch[x][!k]; 14 if (nroot(y)) ch[z][ch[z][1]==y]=x; 15 ch[x][!k]=y,ch[y][k]=w,f[y]=x,f[x]=z; 16 if (w) f[w]=y; 17 pushup(y); 18 } 19 void splay(int x) { work(x); while (nroot(x)) rotate(x); pushup(x); } 20 void access(int x) { for (int y=0;x;x=f[y=x]) splay(x),s[x]+=size[ch[x][1]],s[x]-=size[ch[x][1]=y],pushup(x); } 21 void makeroot(int x) { access(x),splay(x),tag[x]^=1; } 22 void split(int x,int y) { makeroot(x),access(y),splay(y); } 23 void link(int x,int y) { split(x,y);s[f[x]=y]+=size[x],pushup(y); } 24 int main() 25 { 26 scanf("%d%d",&n,&m); 27 for (int i=1;i<=n;i++) size[i]=i; 28 for (int x,y;m;m--) 29 { 30 char ch=getchar(); while (ch!='A'&&ch!='Q') ch=getchar(); 31 scanf("%d%d",&x,&y); 32 if (ch=='Q') split(x,y),printf("%lld\n",(s[x]+1)*(s[y]+1)); else link(x,y); 33 } 34 }