HDU 4010 Query on the tree 动态树(link-cut-tree)入门

56 篇文章 0 订阅
4 篇文章 0 订阅


我是在学了splay之后做这题的。  对于树链的操作,感觉旋转一下也没什么问题。   


可以归纳下需要解决的问题。  


一是如何将多叉树,类似的转变成二叉来做。


二是如何高效的确定同一棵树上的两个节点的lca(也就是最近公共祖先)


我学的时候,是看这里的题解的:  http://www.cnblogs.com/kuangbin/archive/2013/09/04/3300251.html


动态树的资料可以参考: 

http://wenku.baidu.com/view/1845fdc75fbfc77da269b1ae.html 

http://wenku.baidu.com/view/e7c220c52cc58bd63186bdae.html  



什么叫access操作?  如果不懂这个,你就无法理解它的巧妙了。


它是将从该节点一直到根节点的这条路径标记出来,修改它的一个标志属性,来表示该节点是已经被选择的。  


题解中的实现方式是

1、如果该点是选择的点,则标记成rt[u]=false

2、如果该点不是选择的点,但却遍历到了,就标记成rt[u]=true;


这样,我们讨论如何如下


1、 如何使一个点变成根

因为你已经把这条路径标记成了 false

则可以使用Splay操作,直接一直旋转到根。


2、如何获得lca(最近公共祖先)

通过u这个节点,标记出一条false 链后,

再通过v这个节点,不断向上标记,如果发现上一层是false的节点,那么它所调用的Splay会直接将该点旋转至于0之下,自动return掉了。 此为相当巧妙的地方。


需要注意的一点是,mroot操作之后需要翻转下左右链。 因为它是从ch[u][1],这条链翻转上去的。。


额。大致来说就是这样。下次得做些别的题目来巩固了。



//
//  hdu4010.cpp
//  ACM_HDU
//
//  Created by ipqhjjybj on 13-9-11.
//  Copyright (c) 2013年 ipqhjjybj. All rights reserved.
//

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAXN 333333
using namespace std;
int n,head[MAXN],cnt;
int pre[MAXN],ch[MAXN][2],add[MAXN],rev[MAXN],Max[MAXN],key[MAXN];
bool rt[MAXN];
int max(int a,int b){
    if(a>b)return a;
    else return b;
}
struct node{
    int to,next;
}Edge[MAXN<<1];
void addEdge(int u,int v){
    Edge[cnt].to=v;
    Edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u){
    for(int q=head[u];q!=-1;q=Edge[q].next){
        int v=Edge[q].to;
        if(pre[v]!=0)continue;
        pre[v]=u;
        dfs(v);
    }
}
void Update_add(int u,int w){
    if(!u)return;
    key[u]+=w;
    add[u]+=w;
    Max[u]+=w;
}
void Update_rev(int u){
    if(!u)return;
    swap(ch[u][1],ch[u][0]);
    rev[u]^=1;
}
void PushUp(int u){
    Max[u]=max(max(Max[ch[u][0]],Max[ch[u][1]]),key[u]);
}
void PushDown(int u){
    if(add[u]){
        Update_add(ch[u][0], add[u]);
        Update_add(ch[u][1],add[u]);
        add[u]=0;
    }
    if(rev[u]){
        Update_rev(ch[u][0]);
        Update_rev(ch[u][1]);
        rev[u]=0;
    }
}
void Rotate(int x){
    int y=pre[x];int kind=(ch[y][1]==x);
    ch[y][kind]=ch[x][!kind];
    pre[ch[y][kind]]=y;
    pre[x]=pre[y];
    pre[y]=x;
    ch[x][!kind]=y;
    if(rt[y])
        rt[y]=false,rt[x]=true;
    else ch[pre[x]][ch[pre[x]][1]==y]=x;
    PushUp(y);
}
void P(int u){
    if(!rt[u]) P(pre[u]);
    PushDown(u);
}
void Splay(int u){
    P(u);
    while(!rt[u]){
        int f=pre[u],ff=pre[f];
        if(rt[f])
            Rotate(u);
        else{
            if((ch[ff][1]==f)==(ch[f][1]==u))
                Rotate(f),Rotate(u);
            else Rotate(u),Rotate(u);
        }
    }
    PushUp(u);
}
int Access(int u){
    int f;
    for(f=0;u;u=pre[f=u]){
        Splay(u);
        rt[ch[u][1]]=true,rt[ch[u][1]=f]=false;
        PushUp(u);
    }
    return f;
}
bool judge(int u,int v){
    while(pre[u])u=pre[u];
    while(pre[v])v=pre[v];
    return u==v;
}
void mroot(int u){
    Access(u);
    Splay(u);
    Update_rev(u);
}
void lca(int &u,int &v){
    Access(v);
    for(v=0;u;u=pre[v=u]){
        Splay(u);
        if(pre[u]==0)return;
        rt[ch[u][1]]=true,rt[ch[u][1]=v]=false;
        PushUp(u);
    }
}
void Link(int u,int v){
    if(judge(u,v)){
        puts("-1");
        return;
    }
    mroot(u);
    pre[u]=v;
}
void Add(int u,int v,int w){
    if(!judge(u,v)){
        puts("-1");
        return;
    }
    lca(u,v);
    Update_add(ch[u][1], w);
    Update_add(v,w);
    key[u]+=w;
    PushUp(u);
}
void Cut(int u,int v){
    if(u==v||!judge(u,v)){
        puts("-1");
        return;
    }
    mroot(u);
    Splay(v);
    pre[ch[v][0]]=pre[v];
    pre[v]=0;
    rt[ch[v][0]]=true;
    ch[v][0]=0;
    PushUp(v);
}
void Query(int u,int v){
    if(!judge(u, v)){
        puts("-1");
        return;
    }
    lca(u,v);
    printf("%d\n",max(max(Max[ch[u][1]],Max[v]),key[u]));
}
int main(){
    int q,cmd,a,b,c;
    while(scanf("%d",&n)!=EOF){
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1,u,v;i<n;i++){
            scanf("%d %d",&u,&v);
            addEdge(u,v);
            addEdge(v,u);
        }
        for(int i=1;i<=n;i++)
            scanf("%d",key+i);
        key[0]=0;
        for(int i=0;i<=n;i++){
            ch[i][0]=ch[i][1]=pre[i]=rev[i]=add[i]=0;
            Max[i]=key[i];
            rt[i]=true;
        }
        Max[0]=-122222;
        pre[1]=-1;
        dfs(1);
        pre[1]=0;
        scanf("%d",&q);
        while(q--){
            scanf("%d",&cmd);
            if(cmd==1){
                scanf("%d %d",&a,&b);
                Link(a,b);
            }else if(cmd==2){
                scanf("%d %d",&a,&b);
                Cut(a,b);
            }else if(cmd==3){
                scanf("%d %d %d",&a,&b,&c);
                Add(b,c,a);
            }else{
                scanf("%d %d",&a,&b);
                Query(a,b);
            }
            //printf("q=%d a=%d b=%d\n",q,a,b);
        }
        printf("\n");
    }
    return 0;
}


下次就完全自己手写了。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值