题意
给你一棵有 \(n\) 个节点的有根点权树,编号 \(1\) 的节点是根节点,有 \(Q\) 次询问,支持下列三种操作
- 将某个点的权值进行修改
- 修改有根树的树根
- 查询某棵子树的最小值
对于 \(100\%\) 的数据,\(1\le n,Q\le 10^5\),\(0\le f_i\le n\),当且仅当 \(i=1 时 f_i=0\),\(0\le v_i\le 10^4\)
思路
首先,考虑如果没有换根操作,那么直接用 ST表 或 线段树 配合 dfs序 维护区间最小值即可。
然后,考虑换根操作,我们考虑某些节点,换根之后,它的子树会不会发生变化。
例如:
情况一:

不难发现,新根替换旧根后,\(x\) 的子树并没有 发生变化,所以按照原根进行计算。
情况二:

不难发现,在这种情况下,\(x\) 的子树根本没有发生变化!所以这 \(2\) 种位置的子树都不会发生改变。
情况三:

在这种情况下,\(x\) 的子树确实发生变化了,其实就是新根子树外的部分
综上所述
- 只有子树的根节点在原根到换之后的根之间的路径上(即子树的根与新根的 lca 为子树的根),才会有变化,减少的部分即为 \(x\) 与新根的路径上新根的儿子 \(y\),新子树即为所有点减去 \(y\) 的子树。
- 反之,则按原根进行计算即可
(上述所有子树都是以 \(1\) 为根节点时的子树)
实现代码
cpp
#include<bits/stdc++.h>
using namespace std;
#define ls (rt<<1)
#define rs (rt<<1|1)
const int N=1e5+5;
int n,m,a[N];
vector<int>e[N];
int times,xua[N],in[N],out[N],d[N],f[N][17],root=1;
inline void dfs(int u,int fa){
in[u]=++times;
xua[times]=a[u];
d[u]=d[fa]+1;
f[u][0]=fa;
for(int i=1;(1<<i)<=d[u];i++){
f[u][i]=f[f[u][i-1]][i-1];
}
for(auto v:e[u]){
if (v==fa)continue;
dfs(v,u);
}
out[u]=times;
}
inline int lca(int x,int y){
if (d[x]>d[y]){
swap(x,y);
}
for(int i=16;i>=0;i--){
if (d[x]<=d[y]-(1<<i)){
y=f[y][i];
}
}
if (x==y){
return x;
}
for(int i=16;i>=0;i--){
if (f[x][i]==f[y][i]){
continue;
}else{
x=f[x][i],y=f[y][i];
}
}
return f[x][0];
}
//x是y的祖先,且x!=y
//返回x到y路径上,x的那个儿子
inline int get_son(int x,int y){
for(int i=16;i>=0;i--){
if (d[f[y][i]]>d[x]){
y=f[y][i];
}
}
return y;
}
struct tree{
int l,r,minn;
}t[N<<2];
inline void pushup(int rt){
t[rt].minn=min(t[ls].minn,t[rs].minn);
}
inline void build(int rt,int l,int r){
t[rt].l=l,t[rt].r=r;
if (l==r){
t[rt].minn=xua[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
inline void update(int rt,int pos,int val){
if (t[rt].l==t[rt].r){
t[rt].minn=val;
return;
}
int mid=(t[rt].l+t[rt].r)>>1;
if (pos<=mid)update(ls,pos,val);
if (mid<pos)update(rs,pos,val);
pushup(rt);
}
inline int query(int rt,int l,int r){
if (l<=t[rt].l and t[rt].r<=r){
return t[rt].minn;
}
int mid=(t[rt].l+t[rt].r)>>1;
int minn=1e9;
if (l<=mid)minn=min(minn,query(ls,l,r));
if (mid<r)minn=min(minn,query(rs,l,r));
return minn;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
int father;
cin>>father>>a[i];
if (father!=0){
e[i].push_back(father);
e[father].push_back(i);
}
}
dfs(1,0);
build(1,1,times);
while(m--){
char op;
cin>>op;
if (op=='V'){
int pos,val;
cin>>pos>>val;
a[pos]=val;
update(1,in[pos],val);
}else if(op=='E'){
int x;
cin>>x;
root=x;
}else{
int x;
cin>>x;
if (x==root){
cout<<query(1,1,times)<<'\n';
}else{
int g=lca(x,root);
if (x!=g){
cout<<query(1,in[x],out[x])<<"\n";
}else{
int son=get_son(x,root);
int ans=1e9;
if (in[son]>1){
ans=min(ans,query(1,1,in[son]-1));
}
if (out[son]<times){
ans=min(ans,query(1,out[son]+1,times));
}
cout<<ans<<"\n";
}
}
}
}
return 0;
}