BZOJ 3083 遥远的国度(树链剖分+线段树)

 

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3083

 

【题目大意】

  链修改,子树最小值查询和换根操作

 

【题解】

  树链剖分练习题。

 

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010,M=N<<2;
const int INF=~0U>>1;
int n,m,op,x,y,z,a[N],seq[N];
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b;int tag,val;}T[M];
    void build(int,int);
    void Initialize(int n){
        tot=0;
        build(1,n);
    } 
    void addtag(int x,int tag){
        T[x].tag=tag;
        T[x].val=tag;
    }
    void pb(int x){
        if(T[x].tag!=0){
            if(T[x].l){addtag(T[x].l,T[x].tag);addtag(T[x].r,T[x].tag);}
            T[x].tag=0;
        }
    }
    void up(int x){T[x].val=min(T[T[x].l].val,T[T[x].r].val);}
    void build(int l,int r){
        int x=++tot;
        T[x].a=l;T[x].b=r;T[x].tag=T[x].l=T[x].r=0;
        if(l==r){T[x].val=a[seq[l]];return;}
        int mid=(l+r)>>1;
        T[x].l=tot+1;build(l,mid);
        T[x].r=tot+1;build(mid+1,r);
        up(x);
    }
    void change(int x,int a,int b,int p){
        if(T[x].a>=a&&T[x].b<=b){addtag(x,p);return;}
        if(T[x].tag)pb(x); 
        int mid=(T[x].a+T[x].b)>>1;
        if(mid>=a&&T[x].l)change(T[x].l,a,b,p);
        if(mid<b&&T[x].r)change(T[x].r,a,b,p);
        up(x);
    }
    int query(int x,int a,int b){
        if(a>b)return INF;
        if(T[x].a>=a&&T[x].b<=b)return T[x].val;
        if(T[x].tag)pb(x);
        int mid=(T[x].a+T[x].b)>>1; int res=INF;
        if(mid>=a&&T[x].l)res=min(res,query(T[x].l,a,b));
        if(mid<b&&T[x].r)res=min(res,query(T[x].r,a,b)); 
        return res;
    } 
}
namespace Tree_Chain_Subdivision{
    int ed,root,d[N],num[N],v[N<<1],vis[N],f[N],g[N<<1];
    int nxt[N<<1],size[N],son[N],st[N],en[N],dfn,top[N];
    void add_edge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void dfs(int x){
        size[x]=1;
        for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
            f[v[i]]=x,d[v[i]]=d[x]+1;
            dfs(v[i]),size[x]+=size[v[i]];
            if(size[v[i]]>size[son[x]])son[x]=v[i];
        }
    }
    void dfs2(int x,int y){
        if(x==-1)return;
        st[x]=++dfn;seq[dfn]=x;top[x]=y;
        if(son[x])dfs2(son[x],y);
        for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
        en[x]=dfn;
    }
    //查询x,y两点的lca
    int lca(int x,int y){
        for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
        return d[x]<d[y]?x:y;
    }
    //x是y的祖先,查询x到y方向的第一个点
    int lca2(int x,int y){
        int t;
        while(top[x]!=top[y])t=top[y],y=f[top[y]];
        return x==y?t:son[x];
    }
    //以root为根对x的子树操作
    int subtree(int x,int n){
        if(x==root){return Segment_Tree::query(1,1,n);}
        if(st[x]>st[root]||en[x]<en[root]){return Segment_Tree::query(1,st[x],en[x]);}
        int y=lca2(x,root);
        return min(Segment_Tree::query(1,1,st[y]-1),Segment_Tree::query(1,en[y]+1,n));
    }
    //对x到y路径上的点进行操作
    void chain(int x,int y,int p){
        for(;top[x]!=top[y];x=f[top[x]]){
            if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
            Segment_Tree::change(1,st[top[x]],st[x],p);
        }if(d[x]<d[y]){int z=x;x=y;y=z;}
        Segment_Tree::change(1,st[y],st[x],p);
        // 如果是边权转点权,则为change(st[y]+1,st[x])
    }
    void Initialize(){ 
        memset(g,dfn=ed=0,sizeof(g));
        memset(v,0,sizeof(v));
        memset(nxt,0,sizeof(nxt));
        memset(son,-1,sizeof(son));
    }
}
int main(){
    using namespace Tree_Chain_Subdivision;
    scanf("%d%d",&n,&m);
    Initialize();
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add_edge(x,y);add_edge(y,x);
    }for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dfs(1);dfs2(1,1);
    Segment_Tree::Initialize(dfn);
    scanf("%d",&root);
    while(m--){
        scanf("%d",&op);
        if(op==1)scanf("%d",&root);
        if(op==2){scanf("%d%d%d",&x,&y,&z);chain(x,y,z);}
        if(op==3){scanf("%d",&x);printf("%d\n",subtree(x,n));}
    }return 0;
}
posted @ 2017-07-19 13:11  forever97  阅读(126)  评论(0编辑  收藏  举报