换根树剖模板

换根其实很简单,我们考虑换根对答案的影响,换根不会对链上信息有任何影响,所以我们只考虑子树方面
1.当前的根root不在询问点u的子树中,表示虽然换了根,但u在以root为根的树中的子树就和在以最初的根的树中的子树一样
2.当前的根在询问点u的子树中,那么u在以root为根的树中的子树就是u到root路径上第一个点在以最初的根的树中的子树之外的部分

这两个部分画图好理解
特判一下u=root 的情况,此时对全局做出操作

例题:
一棵有根树,三个操作
1.换根
2.单点修改
3.查询子树最小值

Code:

#include<bits/stdc++.h>
#define INF 1e9
#define ls tr[k].l
#define rs tr[k].r
#define mid ((ls+rs)>>1)
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=100005;
int a[N],id[N],root=1,ff[N];
namespace segtree{
	struct seg{int l,r,minn;}tr[N<<2];
	inline void pushup(int k){tr[k].minn=min(tr[k<<1].minn,tr[k<<1|1].minn);}
	inline void build(int k,int l,int r){
		ls=l,rs=r;
		if(l==r) {tr[k].minn=a[id[l]];return;}
		build(k<<1,l,mid);build(k<<1|1,mid+1,r);
		pushup(k);
	}
	inline void modify(int k,int pos,int val){
		if(ls==rs) {tr[k].minn=val;return;}
		if(pos<=mid) modify(k<<1,pos,val);
		else modify(k<<1|1,pos,val);
		pushup(k); 
	}
	inline int query(int k,int ql,int qr){
		if(qr<ql) return INF;
		if(qr<ls || ql>rs) return INF;
		if(ql<=ls && rs<=qr) return tr[k].minn;
		if(qr<=mid) return query(k<<1,ql,qr);
		else if(ql>mid) return query(k<<1|1,ql,qr);
		else return min(query(k<<1,ql,mid),query(k<<1|1,mid+1,qr));
	}
};
using namespace segtree;
int n,m;
int vis[N<<1],nxt[N<<1],head[N<<1],tot=0;
int pt[N],siz[N],dep[N],fa[N],hson[N];
int dfn[N],top[N],sign=0;
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
void dfs1(int v){
	pt[v]=siz[v]=1;int maxn=0;
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(pt[y]) continue;
		fa[y]=v;dep[y]=dep[v]+1;
		dfs1(y);
		siz[v]+=siz[y];
		if(siz[y]>maxn) maxn=siz[y],hson[v]=y;
	}
}
void dfs2(int v){
	dfn[v]=++sign;id[sign]=v;
	if(hson[v]) {top[hson[v]]=top[v];dfs2(hson[v]);}
	for(int i=head[v];i;i=nxt[i]) if(!top[vis[i]]) top[vis[i]]=vis[i],dfs2(vis[i]);
}
inline int lca(int x,int y){
	while(top[x]!=top[y])
		dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
	return dep[x]<dep[y]?x:y;
}
inline int find(int f,int s){
	for(int i=head[f];i;i=nxt[i]){
		if(vis[i]==ff[f]) continue;
		if(dfn[vis[i]]<=dfn[root] && dfn[vis[i]]+siz[vis[i]]-1>=root) return vis[i];
	}
	return 0;
}
inline int ask(int u){
	if(u==root) return query(1,1,n);
	else if(lca(u,root)!=u) return query(1,dfn[u],dfn[u]+siz[u]-1);
	int v=find(u,root);
	return min(query(1,1,dfn[v]-1),query(1,dfn[v]+siz[v],n)); 
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		ff[i]=read();a[i]=read();
		if(i!=1) add(ff[i],i),add(i,ff[i]);
	}
	dfs1(1);top[1]=1;dfs2(1);build(1,1,n);
	while(m--){
		char op;
		scanf("%c",&op);
		if(op=='E') root=read();
		else if(op=='V') {int p=read(),v=read();modify(1,dfn[p],v);}
		else cout<<ask(read())<<'\n';
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值