动态树LCT总结

动态树中最关键的一个函数就是Access,利用Access可以把x到树根的路径暴露出来,并把无关的部分剪掉,形成一颗Splay树。利用Splay树,我们能够任意改变树的形态,它们之间的树形结构并不发生改变。严格上来说,树形结构应该是发生了改变,但它们间的相互位置并不变化,这就是Splay的优点。

在求解LCA的问题上,第一次Access(u)保证了u到树根形成了一颗Splay,并清晰地暴露出来。第二次Access(v)则会把v到树根形成一个Splay,并把LCA与u之间的部分剪掉。因为遇到LCA后,就一定有Splay(LCA),所以最后的终止条件一直是LCA。


代码如下:

/*
    SPOJ 375
    Author: gmcather
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

#define maxn 11000

int head[maxn*2],to[maxn*2],nxt[maxn*2];
int cnt,idx[maxn*2],wei[maxn*2],id[maxn];
int val[maxn],Max[maxn],pre[maxn],ch[maxn][2];
bool rt[maxn];

void add(int u,int v,int i,int w){
    to[cnt]=v;nxt[cnt]=head[u];wei[cnt]=w;
    idx[cnt]=i;head[u]=cnt++;
}

void dfs(int u){
    for(int j=head[u];j!=-1;j=nxt[j]){
        int v=to[j];
        if(pre[v])  continue;
        pre[v]=u;id[idx[j]]=v;val[v]=wei[j];
        dfs(v);
    }
}

void push_up(int x){
    Max[x]=max(max(Max[ch[x][0]],Max[ch[x][1]]),val[x]);
}

void Rotate(int x,int f){
    int y=pre[x];
    ch[y][f]=ch[x][!f];
    pre[ch[x][!f]]=y;
    pre[x]=pre[y];
    if(!rt[y])  ch[pre[y]][ch[pre[y]][1]==y]=x;
    else rt[x]=true,rt[y]=false;
    pre[y]=x;
    ch[x][!f]=y;
    push_up(y);
}

void Splay(int x){
    while(!rt[x]){
        if(rt[pre[x]])  Rotate(x,ch[pre[x]][1]==x);
        else{
            int y=pre[x];int f=ch[y][1]==x;
            if(ch[pre[y]][f]==y){
                Rotate(y,f);Rotate(x,f);
            }
            else{
                Rotate(x,f);Rotate(x,!f);
            }
        }
    }
    push_up(x);
}

void Access(int x){
    for(int y=0;x;x=pre[y=x]){
        Splay(x);
        rt[ch[x][1]]=true;rt[ch[x][1]=y]=false;
        push_up(x);
    }
}

int lca(int &u,int &v){
    Access(v);v=0;
    for(;u;u=pre[v=u]){
        Splay(u);
        if(!pre[u]) break;
        rt[ch[u][1]]=true;rt[ch[u][1]=v]=false;
        push_up(u);
    }
    return max(Max[ch[u][1]],Max[v]);
}

int main(){
    freopen("1.txt","r",stdin);
    int t;scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        memset(head,-1,sizeof(head));cnt=0;
        for(int i=1;i<n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,i,c);
            add(b,a,i,c);
        }
        for(int i=0;i<=n;i++){
            pre[i]=ch[i][0]=ch[i][1]=0;
            rt[i]=true;
        }
        pre[1]=-1;
        dfs(1);
        pre[1]=0;
        for(int i=0;i<=n;i++)   Max[i]=val[i];
        Max[0]=-2000000000;
        while(true){
            char s[20];int a,b;
            scanf("%s",s);
            if(s[0]=='D')   break;
            else if(s[0]=='Q'){
                scanf("%d%d",&a,&b);
                printf("%d\n",lca(a,b));
            }
            else if(s[0]=='C'){
                scanf("%d%d",&a,&b);
                val[id[a]]=b;
                push_up(id[a]);
            }
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值