良心的笔者直接给出了题面:
Description
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
Input
第一行两个整数 n, Q ,分别表示树的大小和操作数。
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
Output
对于每个 Q ,输出子树最小值。
Sample Input
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1
Sample Output
1
2
3
4
HINT
对于 100% 的数据:n, Q ≤ 10^5。
解法:
首先,全程用线段树/树状数组维护。可以记录每个节点在dfs序中的编号,以此将一个子树中的所有节点转换成区间上的连续的一段。
对于操作1,我们正常地单点修改
对于操作2,计一个变量表示现在的根节点的编号。
对于操作3,如果当前查询的x不是根节点的祖先,那就正常的区间查询,
否则,我们修改除该子树外,所有的点。(可以画图证明一下,可好玩了~~)
/*
Name: shu
Copyright: author's
Author: wangziyao
Date: 22/01/19 19:01
Description: BZOJ3306
*/
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rep2(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
template<typename T> void read(T &num){
char c=getchar();num=0;T f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
num*=f;
}
template<typename T> void qwq(T x){
if(x>9)qwq(x/10);
putchar(x%10+'0');
}
template<typename T> void write(T x){
if(x<0){x=-x;putchar('-');}
qwq(x);putchar('\n');
}
template<typename T> void chkmin(T &x,T y){x=x<y?x:y;}
int n,q;
struct wzy{
int nxt,vertice;
}edge[100010];
int head[100010];int len=0;
inline void add_edge(int x,int y){
edge[++len].nxt=head[x];edge[len].vertice=y;head[x]=len;return;
}
int id[100010];int siz[100010];int tot=0;int num[100010];
int f[100010][17];int dep[100010];
inline void Pretreatment(int son,int father){
siz[son]=1;id[son]=++tot;f[son][0]=father;dep[son]=dep[father]+1;num[tot]=son;
rep(i,1,16){f[son][i]=f[f[son][i-1]][i-1];}
for(int i=head[son];i;i=edge[i].nxt){
int nop=edge[i].vertice;
Pretreatment(nop,son);siz[son]+=siz[nop];
}
return;
}
int lans=0;
inline int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
rep2(i,16,0){if(dep[f[x][i]]>dep[y]){x=f[x][i];}}
if(f[x][0]==y){lans=x;return y;}
rep2(i,16,0){if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}}
return f[x][0];
}
int co[100010];int val[400010];
inline void build(int ll,int rr,int pos){
if(ll>rr)return;
if(ll==rr){val[pos]=co[num[ll]];return;}
int mid=(ll+rr)>>1;
build(ll,mid,pos<<1);build(mid+1,rr,pos<<1|1);
val[pos]=min(val[pos<<1],val[pos<<1|1]);return;
}
inline void change(int l,int r,int cl,int cw,int pos){
if(l>r)return;
if(l==r){val[pos]=cw;return;}
int mid=(l+r)>>1;
if(cl<=mid){change(l,mid,cl,cw,pos<<1);}
else{change(mid+1,r,cl,cw,pos<<1|1);}
val[pos]=min(val[pos<<1],val[pos<<1|1]);
return;
}
inline int query(int l,int r,int ql,int qr,int pos){
if(l>r)return 0;
if(ql<=l&&r<=qr){return val[pos];}
else{
int mid=(l+r)>>1;int ans=INT_MAX;
if(ql<=mid)chkmin(ans,query(l,mid,ql,qr,pos<<1));
if(qr>mid)chkmin(ans,query(mid+1,r,ql,qr,pos<<1|1));
return ans;
}
}
int main(){
read(n);read(q);
rep(i,1,n){int f,v;read(f);read(v);add_edge(f,i);co[i]=v;}
Pretreatment(1,0);build(1,n,1);
int root=1;
rep(i,1,q){
char ch;int x,y;cin>>ch;read(x);
if(ch=='V'){
read(y);change(1,n,id[x],y,1);
}else if(ch=='E'){
root=x;
}else{
int temp=LCA(x,root);
if(x==root){write(query(1,n,1,n,1));continue;}
if(temp!=x){write(query(1,n,id[x],id[x]+siz[x]-1,1));}
else{write(min(query(1,n,1,id[lans]-1,1),query(1,n,id[lans]+siz[lans],n,1)));}
}
}
return 0;
}