【BZOJ 3083】遥远的国度 树剖

236 篇文章 0 订阅
4 篇文章 0 订阅

一开始看到有换根操作可是把我吓了一跳,我还以为要上top-tree呢,结果一开题解。。。。。。。。。。心塞

如果除去换根操作的话就是一道裸的树链剖分了,没什么难度,但是加上换根操作以后,别怕,其实联系查询的点和现在的根节点只有4种可能(分3种操作),这里的讨论均基于以1为根节点的有根树讨论

1.x==rt                     直接查询整个树

2.rt是x的祖先           直接查询x为根的子树即可

3.x是rt的祖先           令u为x到rt这一段路径离rt最近的节点,答案就是整个树除了u这个子树的最小点权

4.x和rt不是祖孙关系 直接查询x为根的子树即可

然后返现2,4可以一起讨论,求一下lca就好判断了。。。。ok

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 100021
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
using namespace std;
int nu[maxn],F[maxn],p[maxn][20],size[maxn],n,m,son[maxn],head[maxn],rt;
int h[maxn],tot,etot=1,f[maxn],top[maxn],in[maxn],out[maxn],cnt,val[maxn];
struct edge{int v,next;}e[maxn*2];
void adde(int a,int b){e[etot].v=b,e[etot].next=head[a];head[a]=etot++;}
/****************剖分啊*********************/
void dfs1(int u,int fa){
	size[u]=1,f[u]=fa,h[u]=h[fa]+1,p[u][0]=fa;
	for(int i=1;i<=17;i++)p[u][i]=p[p[u][i-1]][i-1];
	for(int v,i=head[u];i;i=e[i].next){
		if((v=e[i].v)==fa)continue;
		dfs1(v,u);
		size[u]+=size[v];
		if(size[v]>size[son[u]])son[u]=v;
	}
}
void dfs2(int u,int fa,int tt){
	nu[u]=++tot,F[tot]=u,in[u]=++cnt,top[u]=tt;
	if(son[u])dfs2(son[u],u,tt);
	for(int v,i=head[u];i;i=e[i].next){
		if((v=e[i].v)==fa||v==son[u])continue;
		dfs2(v,u,v);
	}
	out[u]=cnt;
}
/****************线段树************************/
int Min[maxn*4],lz[maxn*4];
inline void push_down(int u){
	if(!lz[u])return;
	Min[u<<1]=lz[u],Min[u<<1|1]=lz[u];
	lz[u<<1]=lz[u],lz[u<<1|1]=lz[u];
	lz[u]=0;
}
inline void push_up(int u){Min[u]=min(Min[u<<1],Min[u<<1|1]);}
void build(int u,int l,int r){
	if(l==r){Min[u]=val[F[l]];return;}
	int mid=l+r>>1;
	build(ls),build(rs);
	push_up(u);
}
void update(int u,int l,int r,int x,int y,int add){
	if(x==l&&r==y){
		Min[u]=lz[u]=add;
		return;
	}int mid=l+r>>1;
	push_down(u);
	if(x>mid)update(rs,x,y,add);
	else if(y<=mid)update(ls,x,y,add);
	else update(ls,x,mid,add),update(rs,mid+1,y,add);
	push_up(u);
}
int query(int u,int l,int r,int x,int y){
	if(x==l&&r==y)return Min[u];
	int mid=l+r>>1;
	push_down(u);
	if(x>mid)return query(rs,x,y);
	else if(y<=mid)return query(ls,x,y);
	else return min(query(ls,x,mid),query(rs,mid+1,y));
}
/****************************剖分操作******************************/
void change(int a,int b,int c){
	while(top[a]!=top[b]){
		if(h[top[a]]>h[top[b]])swap(a,b);
		update(1,1,n,nu[top[b]],nu[b],c);
		b=f[top[b]];
	}
	if(h[a]>h[b])swap(a,b);
	update(1,1,n,nu[a],nu[b],c);
}
int lca(int a,int b){
	while(top[a]!=top[b]){
		if(h[top[a]]>h[top[b]])swap(a,b);
		b=f[top[b]];
	}return h[a]>h[b] ? b : a;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int a,b,i=1;i<n;i++){
		scanf("%d%d",&a,&b);
		adde(a,b),adde(b,a);
	}
	for(int i=1;i<=n;i++)scanf("%d",val+i);
	dfs1(1,1);dfs2(1,1,1);
	build(1,1,n);
	scanf("%d",&rt);int pos,a,b,c;
	while(m--){
		scanf("%d",&pos);
		if(pos==1){
			scanf("%d",&a);rt=a;
		}else if(pos==2){
			scanf("%d%d%d",&a,&b,&c);
			change(a,b,c);
		}else{
			scanf("%d",&a);
			if(a==rt){
				printf("%d\n",query(1,1,n,1,n));
			}else if(lca(rt,a)!=a){
				printf("%d\n",query(1,1,n,in[a],out[a]));
			}else{
				int b=rt;
				for(int i=17;i>=0;i--)if(h[p[b][i]]>h[a])b=p[b][i];
				int ans=query(1,1,n,1,in[b]-1);
				if(out[b]<n)ans=min(ans,query(1,1,n,out[b]+1,n));
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值