LCT维护子树信息

Posted hocriser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCT维护子树信息相关的知识,希望对你有一定的参考价值。

有些题目,在要求支持link-cut之外,还会在线询问某个子树的信息。LCT可以通过维护虚边信息完成这个操作。

对于LCT上每个节点,维护两个两sz和si,后者维护该点所有虚儿子的信息,前者维护该点的所有信息和。

那么显然有:$si[x]=sum sz[pson]$,$sz[x]=sz[lson]+sz[rson]+si[x]+v[x]$。

其中pson是虚儿子,lson,rson是LCT上的实儿子,v是节点本身的信息。

那么,考虑在哪些操作下需要更新这两个值。

1.access  每次将旧虚儿子从si中减去,加入新虚儿子的贡献。

2.link f[x]=y后,将x的信息计入si[y]。

别的操作要注意pushup。

这样我们维护了sz和si这两个变量,当想查询y的子树的信息和的时候,只需access(y),splay(y)后查询si[y]即可。

 

[BZOJ4530][BJOI2014]大融合

n个点,n-1次加边,最后形成一棵树。加边过程中会询问某条边现在被多少对点(x,y)之间的路径经过。

显然是link的过程中查询子树大小,最后询问的就是(整个连通块的大小-x子树的大小)*(x子树的大小)。

按上面的方法维护子树大小即可。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lc ch[x][0]
 4 #define rc ch[x][1]
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=100010;
10 char op;
11 int n,Q,u,v,sz[N],f[N],ch[N][2],rev[N],si[N];
12 
13 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); }
14 void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+1; }
15 void put(int x){ swap(lc,rc); rev[x]^=1; }
16 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; }
17 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); }
18 
19 void rot(int x){
20     int y=f[x],z=f[y],w=ch[y][1]==x;
21     if (!isrt(y)) ch[z][ch[z][1]==y]=x;
22     f[x]=z; f[y]=x; f[ch[x][w^1]]=y;
23     ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y);
24 }
25 
26 void splay(int x){
27     pd(x);
28     while (!isrt(x)){
29         int y=f[x],z=f[y];
30         if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y);
31         rot(x);
32     }
33     upd(x);
34 }
35 
36 void access(int x){
37     for (int y=0; x; y=x,x=f[x])
38         splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x);
39 }
40 
41 void mkrt(int x){ access(x); splay(x); put(x); }
42 
43 void link(int x,int y){
44     mkrt(x); access(y); splay(y);
45     f[x]=y; si[y]+=sz[x]; upd(y);
46 }
47 
48 ll que(int x,int y){ mkrt(x); access(y); splay(y); return 1ll*(sz[y]-sz[x])*sz[x]; }
49 
50 int main(){
51     scanf("%d%d",&n,&Q);
52     rep(i,1,n) sz[i]=1;
53     while (Q--){
54         scanf(" %c%d%d",&op,&u,&v);
55         if (op==A) link(u,v); else printf("%lld
",que(u,v));
56     }
57     return 0;
58 }

[BZOJ3510]首都

link的同时询问某个连通块的重心,以及所有连通块重心编号的异或和。

仍然考虑用LCT维护子树大小。每次link两个连通块时,先作普通的link操作,然后求出新的连通块的重心。

显然新重心一定在原来两个连通块重心的连线上,split出这条链后在链上二分。

用ls,rs分别维护这条链两端已被二分排除的部分的大小,每次如果发现当前点合法则更新答案,否则往大的子树那边跳。

记得pushdown。多splay以保证复杂度。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lc ch[x][0]
 4 #define rc ch[x][1]
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=100010;
10 char op[10];
11 int n,Q,ans,x,y,sz[N],fa[N],f[N],ch[N][2],rev[N],si[N];
12 
13 int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); }
14 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); }
15 void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+1; }
16 void put(int x){ swap(lc,rc); rev[x]^=1; }
17 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; }
18 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); }
19 
20 void rot(int x){
21     int y=f[x],z=f[y],w=ch[y][1]==x;
22     if (!isrt(y)) ch[z][ch[z][1]==y]=x;
23     f[x]=z; f[y]=x; f[ch[x][w^1]]=y;
24     ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y);
25 }
26 
27 void splay(int x){
28     pd(x);
29     while (!isrt(x)){
30         int y=f[x],z=f[y];
31         if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y);
32         rot(x);
33     }
34     upd(x);
35 }
36 
37 void access(int x){
38     for (int y=0; x; y=x,x=f[x])
39         splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x);
40 }
41 
42 void mkrt(int x){ access(x); splay(x); put(x); }
43 
44 void split(int x,int y){ mkrt(x); access(y); splay(y); }
45 
46 void link(int x,int y){
47     mkrt(x); mkrt(y); f[x]=y; si[y]+=sz[x]; upd(y);
48     x=get(x); y=get(y); split(x,y);
49     int s=n+1,now=y,ls=0,rs=0,lim=sz[y]/2;
50     while (now){
51         push(now); int s1=ls+sz[ch[now][0]],s2=rs+sz[ch[now][1]];
52         if (s1<=lim && s2<=lim) s=min(s,now);
53         if (s1>s2) rs+=sz[ch[now][1]]+si[now]+1,now=ch[now][0];
54             else ls+=sz[ch[now][0]]+si[now]+1,now=ch[now][1];
55     }
56     splay(s); fa[x]=fa[y]=fa[s]=s; ans=ans^x^y^s;
57 }
58 
59 int main(){
60     scanf("%d%d",&n,&Q);
61     rep(i,1,n) ans^=i,sz[i]=1,fa[i]=i;
62     while (Q--){
63         scanf("%s",op);
64         if (op[0]==X) printf("%d
",ans);
65         else if (op[0]==Q) scanf("%d",&x),printf("%d
",get(x));
66             else scanf("%d%d",&x,&y),link(x,y);
67     }
68     return 0;
69 }

[UOJ#207]共价大爷游长沙

支持:往询问集合中添加删除路径,link-cut,询问某条边是否被集合中的所有路径经过。

一个显然的结论是,一条边(x,y)被所有路径经过,当且仅当整棵树以x为根时,所有路径恰有一个端点在y子树内。

于是我们将所有路径的两端都加上一个随机权值(不同路径用不同权值,同一路径两端用同一个权值),再询问y子树内所有点的权值异或和。

若等于所有路径随机权值的异或和则说明所有路径均恰有一端点在子树内。

随机种子要带时间戳否则会被Hack数据卡掉。

 1 #include<cstdio>
 2 #include<ctime>
 3 #include<algorithm>
 4 #define lc ch[x][0]
 5 #define rc ch[x][1]
 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 7 typedef long long ll;
 8 using namespace std;
 9 
10 const int N=100010;
11 int op;
12 int n,Q,ans,x,y,tot,sz[N],fa[N],f[N],ch[N][2],rev[N],si[N],w[N*3],tx[N*3],ty[N*3];
13 
14 int Rand(){ return (rand()<<15)+rand(); }
15 int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); }
16 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); }
17 void upd(int x){ sz[x]=sz[lc]^sz[rc]^si[x]; }
18 void put(int x){ swap(lc,rc); rev[x]^=1; }
19 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; }
20 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); }
21 
22 void rot(int x){
23     int y=f[x],z=f[y],w=ch[y][1]==x;
24     if (!isrt(y)) ch[z][ch[z][1]==y]=x;
25     f[x]=z; f[y]=x; f[ch[x][w^1]]=y;
26     ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y);
27 }
28 
29 void splay(int x){
30     pd(x);
31     while (!isrt(x)){
32         int y=f[x],z=f[y];
33         if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y);
34         rot(x);
35     }
36     upd(x);
37 }
38 
39 void access(int x){
40     for (int y=0; x; y=x,x=f[x])
41         splay(x),si[x]=si[x]^sz[rc]^sz[y],rc=y,upd(x);
42 }
43 
44 void mkrt(int x){ access(x); splay(x); put(x); }
45 void split(int x,int y){ mkrt(x); access(y); splay(y); }
46 void link(int x,int y){ mkrt(x); access(y); splay(y); f[x]=y; si[y]^=sz[x]; }
47 void cut(int x,int y){ split(x,y); f[x]=ch[y][0]=0; upd(y); }
48 void add(int x,int k){ mkrt(x); sz[x]^=k; si[x]^=k; }
49 
50 int main(){
51     srand(time(NULL));
52     scanf("%*d%d%d",&n,&Q);
53     rep(i,2,n) scanf("%d%d",&x,&y),link(x,y);
54     while (Q--){
55         scanf("%d",&op);
56         if (op==1) scanf("%d%d",&x,&y),cut(x,y),scanf("%d%d",&x,&y),link(x,y);
57         if (op==2){
58             tot++; scanf("%d%d",&tx[tot],&ty[tot]); w[tot]=Rand();
59             ans^=w[tot]; add(tx[tot],w[tot]); add(ty[tot],w[tot]);
60         }
61         if (op==3) scanf("%d",&x),ans^=w[x],add(tx[x],w[x]),add(ty[x],w[x]);
62         if (op==4) scanf("%d%d",&x,&y),split(x,y),puts((si[y]==ans)?"YES":"NO");
63     }
64     return 0;
65 }

 

以上是关于LCT维护子树信息的主要内容,如果未能解决你的问题,请参考以下文章

Loj 2230. 「BJOI2014」大融合 (LCT 维护子树信息)

LCT维护子树信息uoj207 共价大爷游长沙

bzoj4530[Bjoi2014]大融合 LCT维护子树信息

bzoj 3779 重组病毒——LCT维护子树信息

[BJOI2014]大融合 LCT维护子树信息

洛谷P4219 [BJOI2014]大融合(LCT,Splay)