洛谷P3979 遙遠的國度
題目描述
\(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 \leq 1000,m \leq 1000\)。
對於另外\(10\%\)的資料,\(n \leq 100000,m \leq 100000\),保證修改為單點修改。
對於另外\(10\%\)的資料,\(n \leq 100000,m \leq 100000\),保證樹為一條鏈。
對於另外\(10\%\)的資料,\(n \leq 100000\),\(m \leq 100000\),沒有修改首都的操作。
對於\(100\%\)的資料,\(n \leq 100000,m \leq 100000\),\(0<\)所有權值 \(\leq 2^31\)。
思路:首先,第二和第三個操作都是樹鏈剖分的基本操作,然後,這個題的難點在換根上,當然,可以用\(LCT\)實現……然而,我不會,所以就只能換一種思路,首先,換一次頂點\(dfs\)一次,建樹一次,肯定會超時。那麼我們來看,一個點成為根結點之後,只會影響到這個點到原來根結點那條路徑上的點,所以,當查詢一個點,跟當前點的\(LCA\)在這條鏈上時,我們就可以腦補一下,查詢區間就是當前根到原根的路徑上的原根的第一個兒子的子樹的補集,然後剩餘的兩種情況,\(LCA\)是原根或當前根等於原根,直接\(modify\)即可。
程式碼:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 100007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int n,m,head[maxn],son[maxn],size[maxn],d[maxn];
int cnt,num,top[maxn],minn[maxn<<2],id[maxn],fa[maxn];
int w[maxn],a[maxn],rt,lazy[maxn<<2];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int v,nxt;
}e[maxn<<1];
inline void ct(int u, int v) {
e[++num].v=v;
e[num].nxt=head[u];
head[u]=num;
}
inline void pushup(int rt) {
minn[rt]=min(minn[ls],minn[rs]);
}
void build(int rt, int l, int r) {
if(l==r) {
minn[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
inline void pushdown(int rt) {
if(lazy[rt]) {
minn[ls]=lazy[ls]=minn[rs]=lazy[rs]=lazy[rt];
lazy[rt]=0;
}
}
void modify(int rt, int l, int r, int L, int R, int val) {
if(L>r||R<l) return;
if(L<=l&&r<=R) {
minn[rt]=val;
lazy[rt]=val;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
modify(ls,l,mid,L,R,val),modify(rs,mid+1,r,L,R,val);
pushup(rt);
}
int cmin(int rt, int l, int r, int L, int R) {
if(L>r||R<l) return 0x7fffffff;
if(L<=l&&r<=R) return minn[rt];
int mid=(l+r)>>1;
int minn=0x7fffffff;
pushdown(rt);
if(L<=mid) minn=min(minn,cmin(ls,l,mid,L,R));
if(R>mid) minn=min(minn,cmin(rs,mid+1,r,L,R));
return minn;
}
void dfs1(int u, int f) {
size[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=f) {
d[v]=d[u]+1;
fa[v]=u;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
a[cnt]=w[u];
top[u]=t;
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
inline int lca(int x, int y) {
int fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
x=fa[fx],fx=top[x];
}
return d[x]>d[y]?y:x;
}
void cal(int x, int y, int val) {
int fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
modify(1,1,cnt,id[fx],id[x],val);
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
modify(1,1,cnt,id[x],id[y],val);
}
int main() {
n=qread(),m=qread();
for(int i=1,u,v;i<n;++i) {
u=qread(),v=qread();
ct(u,v);ct(v,u);
}
for(int i=1;i<=n;++i) w[i]=qread();
rt=qread();
dfs1(1,0);dfs2(1,1);build(1,1,n);
for(int i=1,k,x,y,w;i<=m;++i) {
k=qread();
if(k==1) x=qread(),rt=x;
if(k==2) x=qread(),y=qread(),w=qread(),cal(x,y,w);
if(k==3) {
x=qread();
if(x==rt) {
printf("%d\n",minn[1]);
continue;
}
int lc=lca(x,rt);
if(lc!=x) {
printf("%d\n",cmin(1,1,n,id[x],id[x]+size[x]-1));
continue;
}
if(lc==x) {
int s;
for(int i=head[x];i;i=e[i].nxt) {
int v=e[i].v;
if(id[v]<=id[rt]&&id[v]+size[v]-1>=id[rt]) {
s=v;
break;
}
}
printf("%d\n",min(cmin(1,1,n,1,id[s]-1),cmin(1,1,n,id[s]+size[s],n)));
}
}
}
return 0;
}