LCT link-cut tree Hdu 5002 Tree 2014鞍山网络赛

之前一直听说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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值