遥远的国度(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\n",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\n",min(query(1,1,n,1,id[pos]-1),query(1,1,n,id[pos]+size[pos],n)));
            }
            else printf("%lld\n",query(1,1,n,id[x],id[x]+size[x]-1));//无影响 
        }
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/sto324/p/11246842.html