[2017.4.13] LCT总结
天哪我又有好久没有水博客啦……主要还是太忙
原论文写的不错:
https://wenku.baidu.com/view/75906f160b4e767f5acfcedb.html
这篇博客对LCT的介绍比较详细(本文部分引用,膜一膜大佬):
http://www.cnblogs.com/BLADEVIL/p/3510997.html
这里补充一下自己的理解:
LCT其实不太难,关键是注意到几点:
1.Preferred path的思想类似于树链剖分,只不过一条链的设定不是根据子树大小,而是最近访问,并且这个链是不断变化的,用Splay维护;
2.用splay来维护每条Preferred path,关键字为深度,也就是每棵splay中的点左子树的深度都比当前点小,右节点的深度都比当前节点的深度大;
3.access是所有操作的基础,就像merge之于左偏树,rotate之于平衡树,等等
4.access(X)之后,X是没有Preferred child的
/* access(X):首先由于preferred path的定义,如果一个点被访问,那么这个点到根节点的所有的边都会变成preferred edge,由于每个点只有一个preferred child,所以这个点到根节点路径上的所有的点都会和原来的preferred child断开,连接到这条新的preferred path上。假设访问X点,那么先将X点旋转到对应Auxiliary tree的根节点,然后因为被访问的点是没有preferred child的,所以将Auxiliary tree中根节点(X)与右子树的边断掉,左节点保留,将这个树的path parent旋转到对应Auxiliary tree的根节点,断掉右子树,连接这个点与X点,相当于合并两棵Auxiliary tree,不断地重复这一操作,直到当前X所在Auxiliary tree的path parent为null时停止,表示已经完成当前操作。 */ inline void access(RG int x){ splay(x); while(tree[x].fa){ RG int f=tree[x].fa; splay(f); tree[tree[f].son[1]].root=1; tree[x].root=0;//(bool)tree[].root表示当前点是否是Auxiliary tree的根节点 tree[f].son[1]=x; update(f);//更新f点的信息 splay(x); } }
剩下的操作都是对于access的应用啦~\(≧▽≦)/~这里就不蒯了,翻开大佬博客很清楚
注意link操作必须是把不同的树link起来,同一棵树你怎么link啊啊啊啊啊
注意区间翻转是使深度正确(当前节点作为根了但它本来的深度是大的,只要把所有子树左右儿子翻转就相当于把深度全部都翻转了一遍,就调整成功啦)
关于makeroot的原理(access(X),splay(X),rev[X]^=1): access之后就有了一条root->x的路径对吧,它是有向图对吧,root是这条路径的起点对吧,reverse之后x就成了起点,也就是根
裸题bzoj2049洞穴勘测
蒯一波题面先……指不定哪天就变成权限题了……
/* 辉辉热衷于洞穴勘测。某天,他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴。假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径。洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通道,有时这条通道又会因为某种稀奇古怪的原因被毁。辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。 */ /* 第一行为两个正整数n和m,分别表示洞穴的个数和终端机上出现过的指令的个数。以下m行,依次表示终端机上出现的各条指令。每行开头是一个表示指令种类的字符串s("Connect”、”Destroy”或者”Query”,区分大小写),之后有两个整数u和v (1≤u, v≤n且u≠v) 分别表示两个洞穴的编号。 */ /* 对每个Query指令,输出洞穴u和洞穴v是否互相连通:是输出”Yes”,否则输出”No”。(不含双引号) */ /* 样例输入1 cave.in 200 5 Query 123 127 Connect 123 127 Query 123 127 Destroy 127 123 Query 123 127 样例输入2 cave.in 3 5 Connect 1 2 Connect 3 1 Query 2 3 Destroy 1 3 Query 2 3 */ /* 样例输出1 cave.out No Yes No 样例输出2 cave.out Yes No */ /* 数据说明 10%的数据满足n≤1000, m≤20000 20%的数据满足n≤2000, m≤40000 30%的数据满足n≤3000, m≤60000 40%的数据满足n≤4000, m≤80000 50%的数据满足n≤5000, m≤100000 60%的数据满足n≤6000, m≤120000 70%的数据满足n≤7000, m≤140000 80%的数据满足n≤8000, m≤160000 90%的数据满足n≤9000, m≤180000 100%的数据满足n≤10000, m≤200000 保证所有Destroy指令将摧毁的是一条存在的通道本题输入、输出规模比较大,建议c\c++选手使用scanf和printf进行I\O操作以免超时 */
很简单的动态树操作,只有cut和link,判断在不在同一颗子树(蒯自http://www.cnblogs.com/kuangbin/archive/2013/09/03/3300141.html)
1 #include<cstdio> 2 #include<iostream> 3 #define N 10010 4 #define RG register 5 using namespace std; 6 char op[9]; 7 int m,n,u,v; 8 struct node{ 9 int fa,son[2]; 10 bool rev,root; 11 }tree[N]; 12 inline int gi(){ 13 RG int x=0;RG char c=getchar(); 14 while(c<'0'||c>'9') c=getchar(); 15 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 16 return x; 17 } 18 inline bool inthesametree(RG int x,RG int y){ 19 while(tree[x].fa) x=tree[x].fa; 20 while(tree[y].fa) y=tree[y].fa; 21 return x==y; 22 } 23 inline void update_rev(RG int x){ 24 if(!x) return; 25 swap(tree[x].son[0],tree[x].son[1]); 26 tree[x].rev^=1;//增加翻转标记 27 }//区间翻转的思想类似于线段树的lazy标记,需要时再翻转 28 inline void push_down(RG int x){ 29 if(!tree[x].rev) return; 30 update_rev(tree[x].son[0]); 31 update_rev(tree[x].son[1]); 32 tree[x].rev=0; 33 }//区间翻转使深度正确 34 inline void push(RG int x){ 35 if(!tree[x].root) push(tree[x].fa);//要从根节点开始翻转 36 push_down(x); 37 } 38 inline void rotate(RG int x,RG bool way){ 39 RG int y=tree[x].fa; 40 //RG bool way=(tree[y].son[1]==x); 41 tree[y].son[way]=tree[x].son[way^1]; 42 tree[tree[y].son[way]].fa=y; 43 tree[x].fa=tree[y].fa; 44 tree[y].fa=x; 45 tree[x].son[way^1]=y; 46 if(tree[y].root) tree[y].root=0,tree[x].root=1; 47 else tree[tree[x].fa].son[tree[tree[x].fa].son[1]==y]=x; 48 }//这是一个不需要记录siz的splay…… 49 inline void splay(RG int x){ 50 push(x);//如果右翻转标记要记得先翻转 51 while(!tree[x].root){ 52 RG int f=tree[x].fa; 53 if(tree[f].root){ 54 if(x==tree[f].son[0]) rotate(x,0); 55 else rotate(x,1); 56 } 57 else if(f==tree[tree[f].fa].son[0]){ 58 if(x==tree[f].son[0]){rotate(f,0);rotate(x,0);} 59 else {rotate(x,1);rotate(x,0);} 60 } 61 else{ 62 if(x==tree[f].son[0]){rotate(x,0);rotate(x,1);} 63 else {rotate(f,1);rotate(x,1);} 64 } 65 /*RG int f=tree[x].fa,ff=tree[f].fa; 66 if(tree[f].root) rotate(x); 67 else if((tree[ff].son[1]==f)==(tree[f].son[1]==f)) rotate(f),rotate(x); 68 else rotate(x),rotate(x);*/ 69 } 70 } 71 inline int access(RG int x){ 72 RG int y=0; 73 do{ 74 splay(x); //先把访问节点旋转到根 75 tree[tree[x].son[1]].root=1; //右子树就断掉啦 76 tree[tree[x].son[1]=y].root=0;//上一次的树连过来,他就不是根啦 77 x=tree[y=x].fa; 78 }while(x); 79 return y; 80 } 81 inline void makeitselfroot(RG int x){ 82 access(x); 83 splay(x); 84 update_rev(x);//重要:把一个点旋转到根后,要把左右子树翻转保证左边深度小,右边深度大 85 } 86 inline void link(RG int x,RG int y){ 87 if(inthesametree(x,y)) return; 88 makeitselfroot(x); 89 tree[x].fa=y; 90 } 91 inline void cut(RG int x,RG int y){ 92 if(x==y||!inthesametree(x,y)) return; 93 makeitselfroot(x); 94 splay(y); 95 tree[tree[y].son[0]].fa=tree[y].fa; 96 tree[y].fa=0; 97 tree[tree[y].son[0]].root=1; 98 tree[y].son[0]=0; 99 } 100 int main(){ 101 freopen("2049.in","r",stdin); 102 freopen("2049.out","w",stdout); 103 n=gi();m=gi(); 104 for (RG int i=1;i<=n;++i) 105 tree[i].root=1; 106 while(m--){ 107 scanf("%s",op+1);u=gi();v=gi(); 108 if(op[1]=='C') link(u,v); 109 if(op[1]=='D') cut(u,v); 110 if(op[1]=='Q') inthesametree(u,v)?puts("Yes"):puts("No"); 111 } 112 fclose(stdin); 113 fclose(stdout); 114 return 0; 115 }
裸题bzoj2002弹飞绵羊
题面就不蒯了
1 #include<cstdio> 2 #include<iostream> 3 #define N 200010 4 #define RG register 5 using namespace std; 6 struct node{ 7 bool rev,root; 8 int fa,siz,son[2]; 9 }tree[N]; 10 int m,n,step[N]; 11 inline int gi(){ 12 RG int x=0;RG bool flag=0;RG char c=getchar(); 13 while((c<'0'||c>'9')&&c!='-') c=getchar(); 14 if(c=='-') flag=1,c=getchar(); 15 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 16 return flag?-x:x; 17 } 18 inline void init(){ 19 for (RG int i=0;i<=n;++i){ 20 tree[i].root=1; 21 tree[i].fa=tree[i].son[0]=tree[i].son[1]=-1; 22 } 23 } 24 inline void push_up(RG int x){ 25 if(tree[x].son[0]==-1&&tree[x].son[1]==-1) tree[x].siz=1; 26 else if(tree[x].son[0]==-1) tree[x].siz=tree[tree[x].son[1]].siz+1; 27 else if(tree[x].son[1]==-1) tree[x].siz=tree[tree[x].son[0]].siz+1; 28 else tree[x].siz=tree[tree[x].son[0]].siz+tree[tree[x].son[1]].siz+1; 29 } 30 inline void rotate(RG int x,RG bool way){ 31 if(x<0) return; 32 RG int f=tree[x].fa; 33 if(f<0) return; 34 tree[f].son[way]=tree[x].son[way^1]; 35 tree[x].son[way^1]=f; 36 tree[x].fa=tree[f].fa; 37 tree[f].fa=x; 38 tree[tree[f].son[way]].fa=f; 39 if(tree[f].root) tree[f].root=0,tree[x].root=1; 40 else tree[tree[x].fa].son[tree[tree[x].fa].son[1]==f]=x; 41 push_up(f); 42 } 43 inline void splay(RG int x){ 44 if(x<0) return; 45 while(!tree[x].root){ 46 RG int f=tree[x].fa; 47 if(tree[f].root){ 48 if(x==tree[f].son[0]) rotate(x,0); 49 else rotate(x,1); 50 } 51 else if(tree[tree[f].fa].son[0]==f){ 52 if(x==tree[f].son[0]){rotate(f,0);rotate(x,0);} 53 else {rotate(x,1);rotate(x,0);} 54 } 55 else{ 56 if(x==tree[f].son[0]){rotate(x,0);rotate(x,1);} 57 else {rotate(f,1);rotate(x,1);} 58 } 59 } 60 push_up(x); 61 } 62 inline void access(RG int x){ 63 RG int y=-1; 64 while(x>=0){ 65 splay(x); 66 if(tree[x].son[1]>=0) tree[tree[x].son[1]].root=1; 67 tree[x].son[1]=y; 68 if(tree[x].son[1]>=0) tree[tree[x].son[1]].root=0; 69 push_up(x); 70 x=tree[y=x].fa; 71 } 72 } 73 inline void make_itself_root(RG int x){ 74 if(x<0) return; 75 access(x); 76 splay(x); 77 } 78 inline void link(RG int x,RG int y){ 79 if(x>=0) tree[x].fa=y; 80 } 81 inline int find(RG int x){ 82 if(x<0) return 0; 83 if(tree[x].fa>=0) return find(tree[x].fa)+1; 84 return 0; 85 } 86 inline void cut(RG int x){ 87 if(x<0) return; 88 make_itself_root(x); 89 splay(x); 90 if(tree[x].son[0]>=0) tree[tree[x].son[0]].fa=tree[x].fa; 91 if(tree[x].son[0]>=0) tree[tree[x].son[0]].root=1; 92 tree[x].fa=-1; 93 tree[x].son[0]=-1; 94 push_up(x); 95 } 96 inline void work(){ 97 n=gi();init(); 98 for (RG int i=0;i<n;++i) step[i]=gi(); 99 for (RG int i=0;i<n;++i) 100 if(i+step[i]>n) link(i,n); 101 else link(i,i+step[i]); 102 m=gi(); 103 while(m--){ 104 RG int i=gi();RG int j=gi(); 105 if(i==1){ 106 make_itself_root(j); 107 if(tree[j].son[0]<0) printf("0\n"); 108 else printf("%d\n",tree[tree[j].son[0]].siz); 109 } 110 else{ 111 RG int k=gi(); 112 cut(j); 113 step[j]=k; 114 if(j+step[j]>n) link(j,n); 115 else link(j,j+step[j]); 116 } 117 } 118 } 119 int main(){ 120 freopen("2002.in","r",stdin); 121 freopen("2002.out","w",stdout); 122 work(); 123 fclose(stdin); 124 fclose(stdout); 125 }
思路就是向一个“终点”连边,需要的次数就是左子树大小(深度)……