zoukankan      html  css  js  c++  java
  • 遥远的国度(D12 树链剖分)

    题意

    给定一棵有根树,有三种操作:换根,查询某个子树点权最小值,将一条路径上的点重新赋值

    对于100%的数据,n<=100000,m<=100000,0<=所有权值<231

    题解

    如果不换根的话,就是一道树链剖分的简单题;

    那么换根要如何操作?LCT?

    考虑换根会带来什么影响,首先换根不会改变两个点之间的路径,书上两点的路径是固定的。

    那对于子树呢?

    拿这样一棵树做例子,假设当前根root=4

    那么这些点的子树会改变:它本身的子树变成整棵树,在原树上在他和最初根节点的路径上的点(1,2);其余的不变;

    考虑第二种怎么改变,可以看得出他的子树变成了整棵树减去一棵子树,减去的子树就是他的包含root的儿子;

    也正是这个性质,可以想到在求最小值的时候在dfs序上被减去的子树分成了两段区间,这样这个问题就可解了。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int maxn=100005;
    const ll oo=210000000000;
    int n,m,nowroot;
    int a[maxn],aa[maxn];
    int cnt,head[maxn];
    int dep[maxn],fa[maxn][25],size[maxn],son[maxn];
    int top[maxn],id[maxn];
    int root,ls[maxn<<1],rs[maxn<<1];
    ll mi[maxn<<1],tag[maxn<<1];
    struct edge{
        int y,next;
    }e[maxn<<1];
    
    template<class T>void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    ll min(ll x,ll y){return x<y ? x : y ;}
    
    void add(int x,int y){
        e[++cnt]=(edge){y,head[x]};
        head[x]=cnt;
    }
    
    void dfs(int u){
        size[u]=1;
        for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].y;
            if(v==fa[u][0]) continue;
            fa[v][0]=u;
            dep[v]=dep[u]+1;
            dfs(v);
            size[u]+=size[v];
            if(size[son[u]]<size[v]) son[u]=v;
        }
    }
    
    void dfs(int u,int tp){
        id[u]=++cnt;
        aa[cnt]=a[u];
        top[u]=tp;
        if(!son[u]) return ;
        dfs(son[u],tp);
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].y;
            if(v==fa[u][0]||v==son[u]) continue;
            dfs(v,v);
        }
    }
    
    void update(int rt){
        mi[rt]=min(mi[ls[rt]],mi[rs[rt]]);
    }
    
    void build(int &rt,int l,int r){
        rt=++cnt;tag[rt]=-1;
        if(l==r) {mi[rt]=aa[l];return ;}
        int mid=(l+r)>>1;
        build(ls[rt],l,mid);
        build(rs[rt],mid+1,r);
        update(rt);
    }
    
    void put_tag(int rt,ll val){
        mi[rt]=tag[rt]=val;
    }
    
    void push_down(int rt){
        put_tag(ls[rt],tag[rt]);
        put_tag(rs[rt],tag[rt]);
        tag[rt]=-1;
    }
    
    void modify(int rt,int l,int r,int a_l,int a_r,ll val){
        if(a_l<=l&&r<=a_r){
            put_tag(rt,val);
            return ;
        }
        int mid=(l+r)>>1;
        if(tag[rt]!=-1) push_down(rt);
        if(a_l<=mid) modify(ls[rt],l,mid,a_l,a_r,val);
        if(mid<a_r) modify(rs[rt],mid+1,r,a_l,a_r,val);
        update(rt);
    }
    
    void modify(int x,int y,ll val){
        while(top[x]!=top[y]){
            if(dep[top[x]]>dep[top[y]]) swap(x,y);
            modify(1,1,n,id[top[y]],id[y],val);
            y=fa[top[y]][0];
        }
        if(dep[x]>dep[y]) swap(x,y);
        modify(1,1,n,id[x],id[y],val);
    }
    
    ll query(int rt,int l,int r,int a_l,int a_r){
        if(a_l<=l&&r<=a_r) return mi[rt];
        if(tag[rt]!=-1) push_down(rt);
        int  mid=(l+r)>>1;
        ll ans=oo;
        if(a_l<=mid) ans=min(ans,query(ls[rt],l,mid,a_l,a_r));
        if(mid<a_r) ans=min(ans,query(rs[rt],mid+1,r,a_l,a_r));
        return ans;
    }
    
    int main(){
        read(n);read(m);
        for(int i=1;i<n;i++){
            int x,y;
            read(x);read(y);
            add(x,y);add(y,x);
        }
        for(int i=1;i<=n;i++) read(a[i]);
        read(root);
        dep[root]=1;
        dfs(root);
        cnt=0;
        dfs(root,root);
        cnt=0;
        build(ls[0],1,n);
        for(int i=1;i<=m;i++){
            int op;read(op);
            if(op==1) read(root);
            else if(op==2){
              int x,y;
              ll z;
              read(x);read(y);read(z);
              modify(x,y,z);            
            }
            else {
                int x;read(x);
                if(x==root) printf("%lld
    ",mi[1]);
                //整颗树
                else if(id[root]>id[x]&&id[root]<id[x]+size[x]){//x的子树改变 
                    int pos=root,delt=dep[pos]-dep[x]-1;
                    for(int p=0;delt;delt>>=1,p++)//倍增 
                     if(delt&1) pos=fa[pos][p];
                    printf("%lld
    ",min(query(1,1,n,1,id[pos]-1),query(1,1,n,id[pos]+size[pos],n)));
                }
                else printf("%lld
    ",query(1,1,n,id[x],id[x]+size[x]-1));//无影响 
            }
        }
    }
    View Code
  • 相关阅读:
    Ubuntu 11.10版本下的软件中心安装软件的默认路径
    C++中构造函数调用与申明方式的关系
    VMware Workstation 虚拟机(客户机)创建和主机共享文件夹
    观察者模式——三英雄战吕布
    如何在yarn上运行Hello World(二)
    Cat 客户端如何构建调用链消息树
    Cat 跨线程之 TaggedTransaction 用法和原理分析
    Cat 客户端采用什么策略上报消息树
    Cat 跨线程之 ForkedTransaction 用法和原理分析
    jest for elasticsearch
  • 原文地址:https://www.cnblogs.com/sto324/p/11246842.html
Copyright © 2011-2022 走看看