「BZOJ3306」树 题解

题意

给你一棵有 \(n\) 个节点的有根点权树,编号 \(1\) 的节点是根节点,有 \(Q\) 次询问,支持下列三种操作

  1. 将某个点的权值进行修改
  2. 修改有根树的树根
  3. 查询某棵子树的最小值

对于 \(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;
}