1. 程式人生 > >bzoj 3083: 遙遠的國度(樹上換根操作,樹剖+詢問整個子樹)

bzoj 3083: 遙遠的國度(樹上換根操作,樹剖+詢問整個子樹)

3083: 遙遠的國度Time Limit: 10 Sec Memory Limit: 1280 MB

Description

描述
zcwwzdjn在追殺十分sb的zhx,而zhx逃入了一個遙遠的國度。當zcwwzdjn準備進入遙遠的國度繼續追殺時,守護神RapiD阻攔了zcwwzdjn的去路,他需要zcwwzdjn完成任務後才能進入遙遠的國度繼續追殺。

問題是這樣的:遙遠的國度有n個城市,這些城市之間由一些路連線且這些城市構成了一顆樹。這個國度有一個首都,我們可以把這個首都看做整棵樹的根,但遙遠的國度比較奇怪,首都是隨時有可能變為另外一個城市的。遙遠的國度的每個城市有一個防禦值,有些時候RapiD會使得某兩個城市之間的路徑上的所有城市的防禦值都變為某個值。RapiD想知道在某個時候,如果把首都看做整棵樹的根的話,那麼以某個城市為根的子樹的所有城市的防禦值最小是多少。由於RapiD無法解決這個問題,所以他攔住了zcwwzdjn希望他能幫忙。但zcwwzdjn還要追殺sb的zhx,所以這個重大的問題就被轉交到了你的手上。

Input

第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為根的子樹中的最小防禦值。

Output

對於每個opt=3的操作,輸出一行代表對應子樹的最小點權值。

Sample Input

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

Sample Output

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。

題解

這一題首先肯定要樹剖,可是怎樣處理詢問整個子樹的操作呢,其實在樹剖過程中得到的 p[i],其實對於每個結點 i 的子樹, p[i] 是連續的!

對於換根操作,當然是不能真的換根,我們可以一直以1為根進行參考,對以1為根的樹進行剖分後,以 i 為根的子樹中最小的 p[i]in[i],最大的 p[i]out[i],假如當前樹根為 cur,詢問 u 的子樹:

(1)若 cur==u ,則是詢問整棵樹。

(2)若 cur 不屬於 u 的子樹,那麼此時以 cur 為根的樹中 u 的子樹就是以1為根的樹中 u 的子樹。

(3)若 cur 屬於 u 的子樹,設 tu 的兒子,且在 ucur 的路徑上

那麼畫一畫就知道,查詢範圍應該是 [1,in[t]1],[out[t]+1,n].

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1e5+10;
int v[maxn];

int fa[maxn],top[maxn],son[maxn],num[maxn],p[maxn],dep[maxn],fp[maxn];
//top[v]表示v所在的重鏈的頂端節點,對於u的輕兒子v有top[v]=v,fa[v]表示v的父親節點,num[v]表示以v為根的子樹的節點數,p[v]表示v與其父親節點的連邊線上段樹中的位置,son[v]為v的重兒子,dep為深度

struct node
{
    int mi,lazy;
}seg[maxn*4];

void up(int i)
{
    seg[i].mi=min(seg[i*2].mi,seg[i*2+1].mi);
}
void pushdown(int i)
{
    if(seg[i].lazy)
    {
        seg[i*2].lazy=seg[i*2].mi=seg[i].lazy;
        seg[i*2+1].lazy=seg[i*2+1].mi=seg[i].lazy;
        seg[i].lazy=0;
    }
}
void build(int i,int l,int r)
{
    seg[i].lazy=0;
    if(l==r)
    {
        seg[i].mi=v[fp[l]];
        return;
    }
    int m=(l+r)/2;
    build(i*2,l,m),build(i*2+1,m+1,r);
    up(i);
}

void update(int i,int l,int r,int L,int R,int v)
{
    if(l==L&&r==R)
    {
        seg[i].lazy=v;
        seg[i].mi=v;
        return;
    }
    pushdown(i);
    int m=(L+R)/2;
    if(r<=m) update(i*2,l,r,L,m,v);
    else if(l>m) update(i*2+1,l,r,m+1,R,v);
    else
    {
        update(i*2,l,m,L,m,v);
        update(i*2+1,m+1,r,m+1,R,v);
    }
    up(i);
}

int query(int i,int l,int r,int L,int R)
{
    if(l==L&&r==R)
    {
        return seg[i].mi;
    }
    if(seg[i].lazy) return seg[i].lazy;
    pushdown(i);
    int m=(L+R)/2;
    if(r<=m) return query(i*2,l,r,L,m);
    else if(l>m) return query(i*2+1,l,r,m+1,R);
    else
    {
        return min(query(i*2,l,m,L,m),query(i*2+1,m+1,r,m+1,R));
    }
}

int head[maxn];
struct edge
{
    int to,next;
}e[maxn*2];   //
int tol=0;
void add(int u,int v)
{
    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
}

int in[maxn],out[maxn];
int pos;//線段樹總區間大小
void dfs(int u,int f,int d)
{
    num[u]=1;
    fa[u]=f;
    dep[u]=d;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==f) continue;
        dfs(v,u,d+1);
        if(son[u]==0||num[v]>num[son[u]])
            son[u]=v;
        num[u]+=num[v];
    }
}
void dfs2(int u,int sp)
{
    top[u]=sp;
    if(son[u])
    {
        p[u]=pos;
        fp[pos]=u;
        in[u]=out[u]=pos;
        pos++;
        dfs2(son[u],sp);
        in[u]=min(in[u],in[son[u]]);
        out[u]=max(out[u],out[son[u]]);
    }
    else    //葉子結點
    {
        p[u]=pos;
        fp[pos]=u;
        in[u]=out[u]=pos;
        pos++;
        return;
    }
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])
        {
            dfs2(v,v);
            in[u]=min(in[u],in[v]);
            out[u]=max(out[u],out[v]);
        }
    }
}
void init()
{
    pos=1;
    memset(fa,0,sizeof(fa));
    memset(son,0,sizeof(son));
}
void change(int u,int v,int n,int w)
{
    int f1=top[u],f2=top[v];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        update(1,p[f1],p[u],1,n,w);
        u=fa[f1],f1=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    update(1,p[u],p[v],1,n,w);
}

int f1[maxn][18];
void bfs(int rt)
{
    queue<int> q;
    dep[rt] = 1;
    f1[rt][0] = rt;
    q.push(rt);
    while(!q.empty())
    {
        int t = q.front();
        q.pop();
        for(int i = 1 ; i <= 17 ; i++)
            f1[t][i] = f1[f1[t][i-1]][i-1];
        for(int i = head[t] ; i ; i = e[i].next)
        {
            int v = e[i].to;
            if(v == f1[t][0])continue;
            dep[v] = dep[t]+1;
            f1[v][0] = t;
            q.push(v);
        }
    }
}
int get_up(int u,int k)
{
    int tu=u;
    for(int det = k, i = 0; det ;det>>=1, i++)
        if(det&1)
            tu = f1[tu][i];
    return tu;
}

int main()
{
    init();
    int n,m;
    scanf("%d%d",&n,&m);
    rep(i,1,n+1) in[i]=1e9,out[i]=0;
    rep(i,1,n)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    rep(i,1,n+1) scanf("%d",&v[i]);
    int cur;
    scanf("%d",&cur);
    bfs(1);
    dfs(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    while(m--)
    {
        int op,u,v,w;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d",&cur);
        }
        else if(op==2)
        {
            scanf("%d%d%d",&u,&v,&w);
            change(u,v,n,w);
        }
        else if(op==3)
        {
            scanf("%d",&u);
            int ans=1e9;
            if(u==cur)
                ans=seg[1].mi;
            else if(in[u]<=in[cur]&&out[cur]<=out[u])
            {
                int k=dep[cur]-dep[u]-1;
                int tv=get_up(cur,k);
                if(in[tv]>1)
                ans=min(ans,query(1,1,in[tv]-1,1,n));
                if(out[tv]<n)
                ans=min(ans,query(1,out[tv]+1,n,1,n));
            }
            else
                ans=query(1,in[u],out[u],1,n);
            printf("%d\n",ans);
        }
    }
    return 0;
}