之前一直听说link-cut树感觉非常神奇,一直不敢学习,认为太难了,但确实是比较难。
但是好奇心还是比较强的,但是网上的信息不多,学习比较麻烦。这里不仅写个解题报告,也写个lct的讲解
由于这个暑假集训的时候我看完树链剖分,splay
树链剖分会线段树以后就容易了,网上有很多写的好的博客
splay可以看《伸展树的基本操作与应用》
然后就是lct了,《QTREE解法的一些研究》把基本的操作和证明都讲的很详细。
但是呢,我还是没有很明白具体的操作。后来看到一个解题报告,代码写的很好,于是明白了具体操作的方法,
//http://www.cnblogs.com/oyking/p/3979239.html
splay的基本操作时
rotate(x)将一个结点旋转到父亲的位置
splay(x)将结点x旋转到所在splay树的根位置
讲解一下
splay的操作为什么有rotate(x),rotate(x)和rotate(f),rotate(x) 其中f是x的父亲
根据上面的图的右下角部分,最后一个结点的情况通过fx旋转以后变成左上角的情况
左上角的情况通过xx旋转以后变成右上角的情况,于是这种方式的旋转最终使得splay树层数趋于下降,保证平衡性
link-cut 的基本操作时
access(x)将x到树根连接成一条path,就是是在一棵splay树上,x的孩子都不在这棵splay上!
其实这以上就是基本操作了!基本的功能都可以用这几个操作实现。
当然通常题目需要以下几种功能,就可以实现符合题目的操作了
be_root 将一个结点变成根
link(x,f)x与f结点建立一条边
cut(x,y)删除x,y之间的边
link-cut是动态树,之所以是动态的,跟树链剖分一笔就显得非常明显了
树链剖分的线段树是静态的,但是link-cut中每次操作都会改变一些splay的状态,所以是动态的
note:
一个比较好的细节是nil ----虚拟结点,可以省去好多判断
指针的速度比较快,是个不错的选择
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 300007 #define inf 1000000000 struct Node{ Node *fa,*ch[2]; bool rev,root; int val,add,set,size; int max[2],num[2]; }; Node pool[maxn]; Node *nil,*tree[maxn]; int cnt = 0; void init(){ cnt = 1; nil = tree[0] = pool; nil->size = 0; } Node *newnode(int val,Node *f){ pool[cnt].fa = f; pool[cnt].ch[0] = pool[cnt].ch[1] = nil; pool[cnt].rev = false; pool[cnt].root = true; pool[cnt].val = val; pool[cnt].add = 0; pool[cnt].size = 1; pool[cnt].set = -inf; pool[cnt].max[0] = val; pool[cnt].num[0] = 1; pool[cnt].max[1] = -inf; pool[cnt].num[1] = 0; return &pool[cnt++]; } void getmax(Node *x,int a,int n){ if(n == 0) return ; if(a == x->max[1]) x->num[1] += n; else if(a == x->max[0]) x->num[0] += n; else { if(a > x->max[1]) x->num[1] = n, x->max[1] = a; if(x->max[1] > x->max[0]) swap(x->max[0],x->max[1]),swap(x->num[0], x->num[1]); } } void update_rev(Node *x){ if(x == nil) return ; x->rev = !x->rev; swap(x->ch[0],x->ch[1]); } //splay向上更新信息 void update(Node *x){ x->size = x->ch[0]->size + x->ch[1]->size + 1; x->max[0]=x->val; x->max[1]=-inf; x->num[0]=1; x->num[1]=0; if(x->ch[0] != nil){ getmax(x,x->ch[0]->max[0],x->ch[0]->num[0]); getmax(x,x->ch[0]->max[1],x->ch[0]->num[1]); } if(x->ch[1] != nil){ getmax(x,x->ch[1]->max[0],x->ch[1]->num[0]); getmax(x,x->ch[1]->max[1],x->ch[1]->num[1]); } } void update_set(Node *x,int n){ if(x == nil) return; x->max[1] = -inf; x->num[1] = 0; x->max[0] = x->val = x->set = n; x->num[0] = x->size; x->add = 0; } void update_add(Node *x,int n){ if(x == nil) return ; x->max[0] +=n ; x->val += n; x->add += n; if(x->max[1] != -inf) x->max[1] += n; } //splay下推信息 void pushdown(Node *x){ if(x->set != -inf){ update_set(x->ch[0],x->set); update_set(x->ch[1],x->set); x->set = -inf; } if(x->add != 0){ update_add(x->ch[0],x->add); update_add(x->ch[1],x->add); x->add = 0; } if(x->rev != false){ update_rev(x->ch[0]); update_rev(x->ch[1]); x->rev = false; } } //splay在root-->x的路径下推信息 void push(Node *x){ if(!x->root) push(x->fa); pushdown(x); } //将结点x旋转至splay中父亲的位置 void rotate(Node *x){ Node *f = x->fa, *ff = f->fa; int t = (f->ch[1] == x); if(f->root) x->root = true, f->root = false; else ff->ch[ff->ch[1] == f] = x; x->fa = ff; f->ch[t] = x->ch[t^1]; x->ch[t^1]->fa = f; x->ch[t^1] = f; f->fa = x; update(f); } //将结点x旋转至x所在splay的根位置 void splay(Node *x){ push(x); Node *f, *ff; while(!x->root){ f = x->fa,ff = f->fa; if(!f->root) if((ff->ch[1] == f) && (f->ch[1] == x)) rotate(f); else rotate(x); rotate(x); } update(x); } //将x到树根的路径并成一条path Node *access(Node *x){ Node *y = nil; while(x != nil){ splay(x); x->ch[1]->root = true; (x->ch[1] = y)->root = false; update(x); y = x; x = x->fa; } return y; } //将结点x变成树根 void be_root(Node *x){ access(x); splay(x); update_rev(x); } //将x连接到结点f上 void link(Node *x, Node *f){ be_root(x); x->fa = f; } //将x,y分离 void cut(Node *x,Node *y){ be_root(x); access(x); splay(y); y->fa = nil; } struct Edge{ int v,next; }; Edge edge[maxn]; int head[maxn],ecnt; int value[maxn]; void add_edge(int u,int v){ edge[ecnt].v = v; edge[ecnt].next = head[u]; head[u] = ecnt++; edge[ecnt].v = u; edge[ecnt].next = head[v]; head[v] = ecnt++; } void dfs(int u,int f){ tree[u] = newnode(value[u],tree[f]); for(int i = head[u]; i != -1;i = edge[i].next){ if(edge[i].v == f) continue; dfs(edge[i].v,u); } } int main(){ int t,n,m,u,v,x,y,a,b,d,c; Node *p; scanf("%d",&t); for(int tt = 1;tt <= t; tt++){ printf("Case #%d:\n",tt); memset(head,-1,sizeof(head)); ecnt = 0; init(); scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++){ scanf("%d",&value[i]); } for(int i = 1;i < n; i++){ scanf("%d%d",&u,&v); add_edge(u,v); } dfs(1,0); for(int i = 0;i < m; i++){ scanf("%d",&c); if(c == 1){ scanf("%d%d%d%d",&x,&y,&a,&b); cut(tree[x],tree[y]); link(tree[a],tree[b]); } else if(c == 2){ scanf("%d%d%d",&a,&b,&x); be_root(tree[a]); update_set(access(tree[b]),x); } else if(c == 3){ scanf("%d%d%d",&a,&b,&x); be_root(tree[a]); update_add(access(tree[b]),x); } else { scanf("%d%d",&a,&b); be_root(tree[a]); p = access(tree[b]); if(p->size == p->num[0]) printf("ALL SAME\n"); else printf("%d %d\n",p->max[1],p->num[1]); } } } return 0; }