Link-Cut-Tree模板
2016-03-09 20:14
351 查看
这次不是在很多人的帮助下……而是自己调的代码、、、
略微看了看自己LCT掉进去的几个坑、、、
略微介绍一下:
LCT的类似于树链剖分,只不过树链剖分是静态的,而LCT是动态的。
LCT用Splay来维护每一条重链,而且在初始情况下,LCT是没有轻重链之分的。
请注意,这里Splay维护的是按照深度递增的顺序来进行排序的。
找到一张图是这样的:
![](http://img.blog.csdn.net/20160309194131027)
把这个树变成LCT之后……
![](http://img.blog.csdn.net/20160309194228231)
核心操作:
Access(v):
访问一个节点,其作用是把该点到LCT的根的路径都变成重链,由于我们维护LCT的时候是用的Splay,我们要把v这个节点Splay到根,也就是Splay到LCT的总根,这是一切操作的基础。
Splay操作:
把一个点伸展到它所在重链的根。
请注意一件事情,也是我最初极为困惑的事情。
在我们学习Splay的时候,我们的根节点的父亲是0,然而这里一条重链的根节点的父亲显然不是0,为了区分轻边重链,我们会使得根节点的父亲的左右儿子并不是根节点。
这里我找了网上的一张图。
![](http://img.blog.csdn.net/20160309194414431)
这张图片里面,7的父亲是1,然而1是在浅红色重链里面的,即它是Splay的最左边节点(因为深度递增),所以它是没有左右儿子的,对吧?然而:7的父亲是1,这点却是一个不争的事实。
也就是说,我们通过什么来判断这个点是否是维护这条重链的Splay的根呢?
显然,我们只要看父亲节点的左右儿子里有没有它即可。
FRT(find root寻找原来的树的根)
请注意这里的root并不是LCT的根,而是原来那个树的根,切记切记。
大概我们来想想就是这样:
首先,根节点是深度最小的点。
所以我们考虑Access(v),v这里是任意一个节点。
然后Splay(v),走到它的最左端即可。
MRT(make root换根操作)
请注意,这里的换根操作也是原来的树的根。
所以说我们只需要这样就行了。
网上的图片……
![](http://img.blog.csdn.net/20160309195134200)
我们把u换成v,也就意味着我们在保证其树的父子关系不变的情况下,使得v的深度最小。
那么也就意味着,我们只需要先Access(v),Splay(v),然后打个翻转标记即可。
这里要注意的一点是,如果询问跟固定一个根有关,在询问的时候一定要记住把根换回来……
Findroot可以找到当前原树的根,也就意味着我们把u换成v之后,Findroot的返回值就是v了。
Cut操作:
切断一条边。
显然在Cut的时候不是简单的找到两个点然后一划就行了的。
我们设Cut(u,v)。
MRT(u),Access(v),Splay(v)。
然后我们就会知道u一定在v的左儿子的位置,然后把v的左儿子标成0,u的父亲标成0即可。
Link操作:
连接一条边。
在连接之前我们肯定要先断掉之前的边『否则你的树形结构就被破坏了』。
我们考虑已经Cut完毕了,现在要连接(u,v),那么只需要:
MRT(u),t[u].par = v即可。
当然了……你随便把哪个当作父亲都是可以的。
模板题是弹飞绵羊。
问题在于……
询问的时候能处理什么?
至少能处理一种东西:
节点深度。
Access(v),Splay(v),return t[lc].sz即可。
BZOJ2002
『题意』
有一群弹簧(1~n),它们喜欢弹飞绵羊,每个弹簧可以让绵羊向后弹Ai格,如果绵羊被弹到超过n的范围,就算是被弹飞了,现在可以更改弹簧的后弹绵羊的能力,而有一个人会在某个弹簧上放一个绵羊,给出绵羊经过几次会被弹飞。
分析:
考虑我们虚拟一个节点标号为n+1,如果第i个弹簧能将绵羊弹飞到i + j,那么i - > min(i + j,n + 1)连边。
更改弹簧能力也就意味着Link&&Cut操作,放羊操作相当于询问节点的深度。
这题就这么过去了……第一次写的话调了好长时间。
大致错误犯了四个,第一个是忘记设置t[0]的sz为0,第二个是在询问的时候忘了换根,而至于第三个……
Cut错边了!!!!
Cut的时候一定要清楚你是Cut什么边……显然我们是找到两个相邻的节点去Cut,那么我们需要存一下上一次的x能被弹飞到哪里去。
第四个是比较2的错误,就是我nxt数组一开始取值为(i + nxt[i]),然而实际上那样会完美RE。
应该改为min(i + nxt[i],n + 1)。
模板就这样说完了。
部分转载自百度文库的一篇文章叫做
Link - Cut- Trees,写的很详细『当然可能有些人认为很废话』,感谢。
题库网站的话:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25242#overview
略微注意一下就是sone1是TopTree……
这网站好多错……看着做吧#233
略微调了一道题……
因为边带负权而我的快读一般都不带负数读入……
然后我就愉快地狗带了。
http://www.lydsy.com/JudgeOnline/problem.php?id=2157
BZOJ2157。
其实这道题没必要用LCT,然而就是想练模板……
然后还有一道题。
洞穴勘测Cave……
很水的一道题……也就是练模板了。
下面开始做真正的LCT吧,另开一文好了。
略微看了看自己LCT掉进去的几个坑、、、
略微介绍一下:
LCT的类似于树链剖分,只不过树链剖分是静态的,而LCT是动态的。
LCT用Splay来维护每一条重链,而且在初始情况下,LCT是没有轻重链之分的。
请注意,这里Splay维护的是按照深度递增的顺序来进行排序的。
找到一张图是这样的:
把这个树变成LCT之后……
核心操作:
Access(v):
访问一个节点,其作用是把该点到LCT的根的路径都变成重链,由于我们维护LCT的时候是用的Splay,我们要把v这个节点Splay到根,也就是Splay到LCT的总根,这是一切操作的基础。
Splay操作:
把一个点伸展到它所在重链的根。
请注意一件事情,也是我最初极为困惑的事情。
在我们学习Splay的时候,我们的根节点的父亲是0,然而这里一条重链的根节点的父亲显然不是0,为了区分轻边重链,我们会使得根节点的父亲的左右儿子并不是根节点。
这里我找了网上的一张图。
这张图片里面,7的父亲是1,然而1是在浅红色重链里面的,即它是Splay的最左边节点(因为深度递增),所以它是没有左右儿子的,对吧?然而:7的父亲是1,这点却是一个不争的事实。
也就是说,我们通过什么来判断这个点是否是维护这条重链的Splay的根呢?
显然,我们只要看父亲节点的左右儿子里有没有它即可。
FRT(find root寻找原来的树的根)
请注意这里的root并不是LCT的根,而是原来那个树的根,切记切记。
大概我们来想想就是这样:
首先,根节点是深度最小的点。
所以我们考虑Access(v),v这里是任意一个节点。
然后Splay(v),走到它的最左端即可。
MRT(make root换根操作)
请注意,这里的换根操作也是原来的树的根。
所以说我们只需要这样就行了。
网上的图片……
我们把u换成v,也就意味着我们在保证其树的父子关系不变的情况下,使得v的深度最小。
那么也就意味着,我们只需要先Access(v),Splay(v),然后打个翻转标记即可。
这里要注意的一点是,如果询问跟固定一个根有关,在询问的时候一定要记住把根换回来……
Findroot可以找到当前原树的根,也就意味着我们把u换成v之后,Findroot的返回值就是v了。
Cut操作:
切断一条边。
显然在Cut的时候不是简单的找到两个点然后一划就行了的。
我们设Cut(u,v)。
MRT(u),Access(v),Splay(v)。
然后我们就会知道u一定在v的左儿子的位置,然后把v的左儿子标成0,u的父亲标成0即可。
Link操作:
连接一条边。
在连接之前我们肯定要先断掉之前的边『否则你的树形结构就被破坏了』。
我们考虑已经Cut完毕了,现在要连接(u,v),那么只需要:
MRT(u),t[u].par = v即可。
当然了……你随便把哪个当作父亲都是可以的。
模板题是弹飞绵羊。
问题在于……
询问的时候能处理什么?
至少能处理一种东西:
节点深度。
Access(v),Splay(v),return t[lc].sz即可。
BZOJ2002
『题意』
有一群弹簧(1~n),它们喜欢弹飞绵羊,每个弹簧可以让绵羊向后弹Ai格,如果绵羊被弹到超过n的范围,就算是被弹飞了,现在可以更改弹簧的后弹绵羊的能力,而有一个人会在某个弹簧上放一个绵羊,给出绵羊经过几次会被弹飞。
分析:
考虑我们虚拟一个节点标号为n+1,如果第i个弹簧能将绵羊弹飞到i + j,那么i - > min(i + j,n + 1)连边。
更改弹簧能力也就意味着Link&&Cut操作,放羊操作相当于询问节点的深度。
这题就这么过去了……第一次写的话调了好长时间。
#include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #define Rep(i,n) for(int i = 1; i <= n ; i ++) #define Repl(i,n) for(int i = 1,ls; i <= n ; i ++) #define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next) #define Rep_d(i,n) for(int i = n ; i > 0 ; i --) #define Rep_0(i,n) for(int i = 0 ; i < n ; i ++) #define RD(i,x,n) for(int i = x; i <= n ; i ++) #define CLR(a,b) memset(a,b,sizeof(a)) #define RDD(i,x,n) for(int i = x; i >= n; i --) #define u t[x] #define debug puts("**") #define debug2 puts("404 NOT FOUND") #define lc ch[0] #define rc ch[1] #define tc ch[ty] #define vc ch[!ty] #define v edge[i].to #define ulfc t[u.lc] #define o t[y] #define urtc t[u.rc] #define p u.par const int N = 200005; using namespace std; const int inf = 1 << 30; typedef long long ll; int read(){ char ch = getchar(); while(ch < '0' || ch > '9')ch = getchar (); int x = 0; while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar (); return x; } int n,m; struct LCT{ int ch[2],par,sz; bool rev; LCT(){sz = 1,ch[0] = ch[1] = par = 0;} void Rev(){rev ^= 1;swap(lc,rc);} }t ; int sgn(int x){return t[p].lc == x ? 0 : t[p].rc == x ? 1 : -1;} void sc(int x,int y,bool ty){u.tc = y,o.par = x;} void Upd(int x){if(x)u.sz = ulfc.sz + urtc.sz + 1;} void Dw(int x){ if(u.rev) ulfc.Rev(),urtc.Rev(),u.rev ^= 1; } void Fix(int x){if(~sgn(x))Fix(p);Dw(x);} void Rot(int x,bool ty){ int y = p; //printf("ROTING:%d %d %d %d\n",x,y,o.par,ty); if(~sgn(y))sc(o.par,x,sgn(y)); else p = o.par; sc(y,u.vc,ty),sc(x,y,!ty),Upd(y); } int Splay(int x){ Fix(x); int d0,d1,y; //printf("x : %d x的父亲%d\n",x,p); //debug2; while((~(d0 = sgn(x)))){ if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1); else Rot(x,d0); } //Rep_0(i,n + 2) // printf("~~~%d %d %d %d~~\n",i,t[i].par,t[i].ch[0],t[i].ch[1]); return Upd(x),x; } int nxt ; int Acs(int tx){ // puts("NOW : ACS"); for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y; // puts("ACS : ENDED"); // Rep_0(i,n + 2) // printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz); return Splay(tx); } void MRT(int x){x = Acs(x);u.Rev();} void Link(int x,int y){MRT(x),p = y;} void DB(){ puts("DEBUGING--------------------------------------------"); Rep_0(i,n + 2) printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz); puts("END-------------------------------------------"); } void Cut(int x,int y){MRT(x),y = Acs(y),o.lc = 0,u.par = 0;} int main (){ // freopen("a.txt","r",stdin); t[0].sz = 0;//TMD初始化 n = read(); Repl(i,n) nxt[i] = read(),t[i].par = min(i + nxt[i],n + 1),nxt[i] = min(nxt[i] + i,n + 1); // DB(); //Rep_0(i,n + 2) // printf("%d %d\n",i,t[i].par); m = read(); // DB(); Rep(i,m) { int op = read(),x = read(); // puts("-------------------------------------------------------"); x ++; if(op & 1){ // DB(); MRT(n + 1);//为什么不换根呢少年!!!!!! x = Acs(x); printf("%d\n",ulfc.sz); // Rep_0(i,n + 2) // printf("lalala: %d %d\n",i,t[i].sz); } else { int k = read(); // DB(); x = Acs(x);//printf("CUT:%d %d\n",x,x + k); // DB(); // printf("%d NXT:%d\n",x,nxt[x]); Cut(x,nxt[x]);//你要CUT 的不是你的左儿子啊!!!! nxt数组爆了啊!!!! // printf("%d NXT : %d\n",x,nxt[x]); // DB(); Link(x,min(x + k,n + 1)); nxt[x] = min(x + k,n + 1); // printf("%d NXT : %d\n",x,nxt[x]); // DB(); // Rep_0(i,n + 2) // printf("***%d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1]); // printf("%d %d\n",x,x + k); } } return 0; } /* 10 4 5 4 3 1 3 1 1 1 3 5 2 0 2 2 7 1 2 5 7 1 6 2 5 6 10 6 7 10 2 5 5 4 4 10 7 10 2 2 1 2 2 4 */
大致错误犯了四个,第一个是忘记设置t[0]的sz为0,第二个是在询问的时候忘了换根,而至于第三个……
Cut错边了!!!!
Cut的时候一定要清楚你是Cut什么边……显然我们是找到两个相邻的节点去Cut,那么我们需要存一下上一次的x能被弹飞到哪里去。
第四个是比较2的错误,就是我nxt数组一开始取值为(i + nxt[i]),然而实际上那样会完美RE。
应该改为min(i + nxt[i],n + 1)。
模板就这样说完了。
部分转载自百度文库的一篇文章叫做
Link - Cut- Trees,写的很详细『当然可能有些人认为很废话』,感谢。
题库网站的话:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25242#overview
略微注意一下就是sone1是TopTree……
这网站好多错……看着做吧#233
略微调了一道题……
因为边带负权而我的快读一般都不带负数读入……
然后我就愉快地狗带了。
http://www.lydsy.com/JudgeOnline/problem.php?id=2157
BZOJ2157。
#include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #define Rep(i,n) for(int i = 1; i <= n ; i ++) #define Repl(i,n) for(int i = 1,ls; i <= n ; i ++) #define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next) #define Rep_d(i,n) for(int i = n ; i > 0 ; i --) #define Rep_0(i,n) for(int i = 0 ; i < n ; i ++) #define RD(i,x,n) for(int i = x; i <= n ; i ++) #define CLR(a,b) memset(a,b,sizeof(a)) #define RDD(i,x,n) for(int i = x; i >= n; i --) #define u t[x] #define debug puts("**") #define debug2 puts("404 NOT FOUND") #define DEBUG puts("FREOPENING") #define lc ch[0] #define rc ch[1] #define tc ch[ty] #define vc ch[!ty] #define v edge[i].to #define ulfc t[u.lc] #define o t[y] #define urtc t[u.rc] #define p u.par using namespace std; const int inf = 1 << 30; const int N = 200005; typedef long long ll; int read(){ char ch = getchar(); while((ch < '0' || ch > '9')&& ch != '-')ch = getchar (); int x = 0,flag = 1; if(ch == '-')flag = -1,ch = getchar(); while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar (); return x * flag; } int n,m,ed ,idx; struct LCT{ int w,Max,Min,ch[2],par; ll sum; bool rev; bool tag; LCT(){w = sum = tag = 0;Max = -inf,Min = inf;} void Rev(){rev ^= 1;swap(lc,rc);} void TagR(){ tag ^= 1;swap(Max,Min),w = -w,sum = - sum,Max = -Max,Min = -Min; } void TagW(int ps){ sum -= w,w = ps,sum += w; } }t[N << 2]; void DB(){/*puts("DEBUGING--------------------------------------------------");*/Rep_0(x,idx + 1)printf("x: %d sum: %lld w: %d lc: %d rc: %d p: %d\n",x,u.sum,u.w,u.lc,u.rc,p);} void Upd(int x){ u.sum = ulfc.sum + urtc.sum + u.w; u.Max = max(ulfc.Max,urtc.Max); if(x > n)u.Max = max(u.w,u.Max); u.Min = min(urtc.Min,ulfc.Min); if(x > n)u.Min = min(u.Min,u.w); } void Dw(int x){ if(u.rev){ ulfc.Rev(),urtc.Rev();u.rev ^= 1; } if(u.tag){ ulfc.TagR(),urtc.TagR();u.tag ^=1; } } int sgn(int x){return t[p].lc == x ? 0 :t[p].rc == x ? 1 : -1;} void sc(int x,int y,bool ty){u.tc = y;o.par = x;} void Fix(int x){if(~sgn(x))Fix(p);Dw(x);} void Rot(int x,bool ty){ int y = p; if(~sgn(y))sc(o.par,x,sgn(y)); else p = o.par; sc(y,u.vc,ty),sc(x,y,!ty),Upd(y); } int Splay(int x){ Fix(x); int d0,d1,y; while((~(d0 = sgn(x)))){ if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1); else Rot(x,d0); } return Upd(x),x; } int Acs(int tx){ for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y; return Splay(tx); } void MRT(int x){Acs(x);u.Rev();}// 先Rev? void link(int x,int y){MRT(x),p = y;} int main (){ // freopen("lv.txt","r",stdin); // freopen("outp.txt","w",stdout); n = read(); idx = n; Rep(i,n - 1){ int a,b,c; ed[i] = ++ idx; a = read() + 1,b = read() + 1,c = read(); link(a,idx),link(b,idx); t[idx].w = c; t[idx].Max = c; t[idx].Min = c; t[idx].sum = c; } m = read(); char op[5]; Rep(i,m){ scanf("%s",op + 1); int a = read(),b = read(); if(op[1] == 'C') Splay(ed[a]),t[ed[a]].w = b,Upd(ed[a]); else if(op[1] == 'N'){ // a ++ ,b ++; MRT(a); Acs(b); t[b].TagR(); } else{ // Sum a ++ , b ++; MRT(a); Acs(b); if(op[1] == 'S')//Sum printf("%lld\n",t[b].sum); else if(op[2] == 'I')// Min printf("%d\n",t[b].Min); else if(op[2] == 'A') printf("%d\n",t[b].Max); } } return 0; } /* 5 0 1 32 0 4 45 0 3 37 1 2 76 3 N 0 3 SUM 0 1 MIN 0 2 */
其实这道题没必要用LCT,然而就是想练模板……
然后还有一道题。
#include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #define Rep(i,n) for(int i = 1; i <= n ; i ++) #define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next) #define Rep_d(i,n) for(int i = n ; i > 0 ; i --) #define Rep_0(i,n) for(int i = 0 ; i < n ; i ++) #define RD(i,x,n) for(int i = x; i <= n ; i ++) #define CLR(a,b) memset(a,b,sizeof(a)) #define RDD(i,x,n) for(int i = x; i >= n; i --) #define lc ch[0] #define rc ch[1] #define tc ch[ty] #define vc ch[!ty] #define u t[x] #define o t[y] #define p t[x].par #define v edge[i].to #define ulfc t[u.lc] #define urtc t[u.rc] const int N = 10005; using namespace std; const int inf = 1 << 30; typedef long long ll; inline int read(){ char ch = getchar(); while((ch < '0' || ch > '9') && ch != '-')ch = getchar (); int x = 0; bool flag = 1; if(ch == '-')ch = getchar(),flag = 0; while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar (); return flag ? x : -x; } int n,m; struct LCT{int ch[2],par;bool rev;void Rev(){rev ^= 1;swap(lc,rc);}}t ; inline int sgn(int x){return t[p].ch[0] == x ? 0: t[p].ch[1] == x ? 1 : -1;} inline void sc(int x,int y,bool ty){o.par = x;u.tc = y;} inline void Upd(int x){;} inline void Rot(int x,bool ty){ int y ; if(~sgn(y = p))sc(o.par,x,sgn(y)); else p = o.par; sc(y,u.vc,ty),sc(x,y,!ty),Upd(y); } inline void Dw(int x){ if(u.rev)ulfc.Rev(),urtc.Rev(),u.rev ^= 1; } inline void Fix(int x){if(~sgn(x))Fix(p);Dw(x);} int Splay(int x){ Fix(x); int d0,d1,y; while(~(d0 = sgn(x))){ if(~(d1 = sgn(y = p))) Rot(d0 ^ d1 ? x : y, d0),Rot(x,d1); else Rot(x,d0); } return Upd(x),x; } int Acs(int tx){for(int x = tx,y = 0 ;x;Upd(y = x),x = p)Splay(x),u.rc = y;return Splay(tx);} inline void MRT(int x){Acs(x);u.Rev();} inline void Link(int x,int y){MRT(x);p = y;} inline void Cut(int x,int y){MRT(x);Acs(y);o.lc = 0,u.par = 0;} inline bool Find(int x,int y){while(p)x = p;while(o.par)y = o.par;return x == y;} int main (){ n = read(); m = read(); Rep(i,m) { char op[15]; scanf("%s",op + 1); int a = read(),b = read(); if(op[1] == 'Q') puts(Find(a,b) ? "Yes" : "No"); else if(op[1] == 'C') Link(a,b); else if(op[1] == 'D') Cut(a,b); } return 0; }
洞穴勘测Cave……
很水的一道题……也就是练模板了。
下面开始做真正的LCT吧,另开一文好了。
相关文章推荐
- 168. Excel Sheet Column Title
- JBL leetcode 165. Compare Version Numbers
- (有码)创建单例
- 【JAVA】34、对象引用传递
- c++如何将string 转换为char*
- 232. Implement Queue using Stacks
- C语言连接mysql数据库
- 好用的在线编译/调试工具
- poj2115——C Looooops(数论,解模线性方程)
- 第 3 章 流程控制语句
- 《我是一只IT小小鸟》读后感
- Mininet学习笔记
- 脚本——生成验证码的函数
- mysql 的简单优化
- 作业-01
- Leetcode: 107. Binary Tree Level Order Traversal II(JAVA)
- scott账户已被锁定,解锁方法
- python学习:机器学习下的pybrain和多线程编程
- 【Http专题】Https
- 个人项目一改进