遥远的国度(树链剖分,换根)

Posted hhh1109

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了遥远的国度(树链剖分,换根)相关的知识,希望对你有一定的参考价值。

遥远的国度

题目描述

zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。

问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。

RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。

由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。

输入输出格式

输入格式:

第1行两个整数n m,代表城市个数和操作数。

第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。

第n+1行,有n个整数,代表所有点的初始防御值。

第n+2行一个整数 id,代表初始的首都为id。

第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。

输出格式:

对于每个opt=3的操作,输出一行代表对应子树的最小点权值。

输入输出样例

输入样例#1: 复制

3 7
1 2
1 3
1 2 3
1
3 1
2 1 1 6
3 1
2 2 2 5
3 1
2 3 3 4
3 1

输出样例#1: 复制

1
2
3
4

说明

对于20%的数据,n<=1000 m<=1000。

对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。

对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。

对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。

对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。


题解


终于填了树链剖分换根这个坑了。
就是换根的时候的我们要分类讨论而不是真的去换根。
有三种情况。

第一种就是询问的点就是当前根。
那么直接输出整棵树的最小值就可以了。

第二种就是当前根在询问的点的子树内。
那么我们首先可以发现。
假如当前根就是询问点的直接连边的儿子的话。
就相当于在询问点的子树去掉当前根的子树求值。
那么多次换根就等于询问的点到当前根的路径上的所有子树都去掉。
而这些子树又奇妙的全包括在询问的点到当前根的路径上的第一个节点。只要把这个点求出来并去掉其子树就可以了。

第三种就是当前根不在询问的点的子树内。
那么它的子树还是原来的子树。


代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1000001;
const int inf=(1ll<<31)-1;
struct node{
    int to,nex;
}e[N];
int num,head[N],sum[N],lazy[N];
int n,m,root,dep[N],a[N],top[N];
int fa[N],son[N],size[N],pos[N],ch[N];
int f[N][21],tot;
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void add(int from,int to){
    num++;
    e[num].to=to;
    e[num].nex=head[from];
    head[from]=num;
}

void build(int root,int left,int right){
    if(left==right){
        sum[root]=a[left];
        return ;
    }
    int mid=(left+right)>>1;
    build(root<<1,left,mid);build(root<<1|1,mid+1,right);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

void push(int root,int left,int right){
    lazy[root<<1]=lazy[root<<1|1]=lazy[root];
    sum[root<<1]=sum[root<<1|1]=lazy[root];
    lazy[root]=0;
}

void update(int root,int left,int right,int l,int r,int v){
    if(left>r||right<l)return ;
    if(left>=l&&right<=r){
        lazy[root]=sum[root]=v;
        return ;
    }
    if(lazy[root])push(root,left,right);
    int mid=(left+right)>>1;
    if(mid>=l) update(root<<1,left,mid,l,r,v);
    if(mid<r)  update(root<<1|1,mid+1,right,l,r,v);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

int query(int root,int left,int right,int l,int r){
    if(left>r||right<l)return inf;
    if(lazy[root])push(root,left,right);
    if(left>=l&&right<=r)return sum[root];
    int mid=(left+right)>>1;int ans1=inf,ans2=inf;
    if(mid>=l) ans1=query(root<<1,left,mid,l,r);
    if(mid<r)  ans2=query(root<<1|1,mid+1,right,l,r);
    return min(ans1,ans2);
}

void dfs1(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(!dep[v]){
            dep[v]=dep[x]+1;fa[v]=x;
            f[v][0]=x;
            dfs1(v);size[x]+=size[v];
            if(size[son[x]]<size[v])son[x]=v;
        }
    }
}

void dfs2(int x,int tp){
    pos[x]=++tot;a[tot]=ch[x];top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v!=fa[x]&&v!=son[x])dfs2(v,v);
    }
}

void init(){
    for(int i=1;i<=17;i++)
        for(int j=1;j<=n;j++)
        f[j][i]=f[f[j][i-1]][i-1];
}

void cal(int x,int y,int v){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
        update(1,1,n,pos[fx],pos[x],v);
        x=fa[fx];fx=top[x];
    }
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,pos[x],pos[y],v);
}

int cal2(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
        x=fa[fx];fx=top[x];
    }
    if(dep[x]>dep[y])return y;
    return x;
}

int find(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    if(f[x][0]==y)return x;
    for(int j=17;j>=1;j--)if(dep[f[x][j]]>=dep[y])x=f[x][j];
    if(f[x][0]==y)return x;
    for(int j=17;j>=0;j--)if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j];
    return x;
}

int main(){
    n=read();m=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)ch[i]=read();
    root=read();
    fa[root]=root;dep[root]=1;
    dfs1(root);dfs2(root,root);build(1,1,n);init();
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==1)root=read();
        if(opt==2){int x=read(),y=read(),z=read();cal(x,y,z);}
        if(opt==3){
            int x=read();int lca=cal2(x,root);
            if(root==x){printf("%d
",query(1,1,n,1,n));continue;}
            if(lca!=x){printf("%d
",query(1,1,n,pos[x],pos[x]+size[x]-1));continue;}
            if(lca==x){
                int LCA=root;
                for(int i=17;i>=0;i--)
                if(dep[f[LCA][i]]>dep[x])LCA=f[LCA][i];
                int ans=min(query(1,1,n,1,pos[LCA]-1),query(1,1,n,pos[LCA]+size[LCA],n));
                printf("%d
",ans);
            }
        }
    }
    return 0;
}



以上是关于遥远的国度(树链剖分,换根)的主要内容,如果未能解决你的问题,请参考以下文章

[日常摸鱼]bzoj3083遥远的国度-树链剖分

bzoj3083: 遥远的国度

洛谷 P3979 遥远的国度(树链剖分)

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

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

遥远的国度