1. 程式人生 > >遙遠的國度(樹鏈剖分,換根)

遙遠的國度(樹鏈剖分,換根)

100% lca 成了 修改 由於 nod ccf -- date

遙遠的國度

題目描述

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\n",query(1,1,n,1,n));continue;}
            if(lca!=x){printf("%d\n",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\n",ans);
            }
        }
    }
    return 0;
}

遙遠的國度(樹鏈剖分,換根)