• 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
    ",subtree(x,n));}
        }return 0;
    }
  • 相关阅读:
    (三)索引分区知识详解
    (二)SQL Server分区创建过程
    (一)SQL Server分区详解Partition(目录)
    表格重新加载 where 携带上次值问题
    MongoDB 时差问题问题
    WebAPI跨域处理
    Http请求中 content-type 和 dataType 区别
    那些坑
    微信公众平台开发系列一 ~ 接入前的配置工作
    MVC中登录页图片验证码总结
  • 原文地址:https://www.cnblogs.com/forever97/p/bzoj3083.html
Copyright © 2020-2023  润新知