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。
題解:
這一題首先肯定要樹剖,可是怎樣處理詢問整個子樹的操作呢,其實在樹剖過程中得到的 ,其實對於每個結點 的子樹, 是連續的!
對於換根操作,當然是不能真的換根,我們可以一直以1為根進行參考,對以1為根的樹進行剖分後,以 為根的子樹中最小的 為 ,最大的 為 ,假如當前樹根為 ,詢問 的子樹:
(1)若 ,則是詢問整棵樹。
(2)若 不屬於 的子樹,那麼此時以 為根的樹中 的子樹就是以1為根的樹中 的子樹。
(3)若 屬於 的子樹,設 為 的兒子,且在 到 的路徑上
那麼畫一畫就知道,查詢範圍應該是 .
#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;
}