bzoj3306:树(dfn序+线段树+倍增)

#Problem
支持换根、修改权值的子树最小值查询

#Solution

不考虑换根就是线段树模板题了…
那么加上换根呢
我们发现换完根,对于原图中大部分子树的最小值是没有关系的
只有 1 1 1 到新根上面的点会有变换
新根:为所有点最小值
除新根外此链上的点:除去新根这个外枝的其他所有点…(emmm画个图看一下)
做法就是 d f n dfn dfn 序建立线段树…倍增找到要除去的部分剩下的部分即为答案。

#Code

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 100010
#define inf 0x3f3f3f3f
int n,q,num=0,tot=0,root,fa[N][20],d[N],h[N],in[N],out[N],rl[N],mn[N<<2],a[N];
char op[4];
struct node{int to,next;}mp[N];
inline void insert(int x,int y){
	mp[++num].to=y;mp[num].next=h[x];h[x]=num;
}
inline void dfs(int x){
	in[x]=++tot;rl[tot]=x;
	for(int i=1;i<=16;i++){
		if(!fa[x][i-1]) break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	} 
	for(int i=h[x];i;i=mp[i].next){
		int y=mp[i].to;if(y==fa[x][0]) continue;
		d[y]=d[x]+1;dfs(y);
	}out[x]=tot;
}
inline void update(int v){
	mn[v]=min(mn[v<<1],mn[v<<1|1]);
}
void build(int v,int l,int r){
	if(l==r){
		mn[v]=a[rl[l]];
		return;
	}int mid=l+r>>1;
	build(v<<1,l,mid);build(v<<1|1,mid+1,r);
	update(v);
}
void change(int v,int l,int r,int x,int y){
	if(l==r){
		mn[v]=y;
		return;
	}int mid=l+r>>1;
	if(x<=mid) change(v<<1,l,mid,x,y); 
	else change(v<<1|1,mid+1,r,x,y);
	update(v);
}
int query(int v,int l,int r,int x,int y){
	if(x>y) return inf;
	if(x<=l && r<=y) return mn[v];
	int mid=l+r>>1,res=inf;
	if(x<=mid) res=min(res,query(v<<1,l,mid,x,y));
	if(mid<y) res=min(res,query(v<<1|1,mid+1,r,x,y));
	return res;
}
int main(){
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&fa[i][0],&a[i]);
		if(i!=1) insert(fa[i][0],i);
	}d[1]=1;dfs(1);build(1,1,n);root=1;

	for(int i=1;i<=q;i++){
		int x,y;scanf("%s%d",op,&x);
		if(op[0]=='V'){
			scanf("%d",&y);
			change(1,1,n,in[x],y);
		}else if(op[0]=='Q'){
			if(in[x]<=in[root] && in[root]<=out[x]){
				if(x==root) printf("%d\n",mn[1]);
				else{
					y=root;
					for(int j=16;j>=0;j--) if(d[fa[y][j]]>d[x]) y=fa[y][j];
					printf("%d\n",min(query(1,1,n,1,in[y]-1),query(1,1,n,out[y]+1,n)));
				}
			}else printf("%d\n",query(1,1,n,in[x],out[x]));
		}else root=x;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值