BZOJ3083: 遥远的国度(换根树剖)

3083: 遥远的国度
Time Limit: 10 Sec Memory Limit: 512 MB
Description
描述
zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。
问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。
Input
第1行两个整数n m,代表城市个数和操作数。
第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。
第n+1行,有n个整数,代表所有点的初始防御值。
第n+2行一个整数 id,代表初始的首都为id。
第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。
Output
对于每个opt=3的操作,输出一行代表对应子树的最小点权值。
Sample Input
3 7
1 2
1 3
1 2 3
1
3 1
2 1 1 6
3 1
2 2 2 5
3 1
2 3 3 4
3 1
Sample Output
1
2
3
4
提示
对于20%的数据,n<=1000 m<=1000。
对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。
对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。
对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。
对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。
HINT
Source
zhonghaoxi提供

这题如果没有操作 1 1 1就是一个裸的树链剖分:操作 2 2 2路径修改,操作 3 3 3维护子树最小值。

那么在加入了操作 1 1 1之后,操作 2 2 2的部分显然没有发生变化
代码:

inline void change(int x,int y,long long v){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		update(1,num[top[x]],num[x],v);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	update(1,num[y],num[x],v);
}

其中 u p d a t e update update是指线段树的区间修改操作, n u m num num数组记录 d f s dfs dfs序。

所以我们只需要考虑换根对维护子树最小值信息的影响,那么我们可以分类讨论一波:

我们设当前询问的子树的根为 u u u,当前的根节点为 r o o t root root

第一种情况: l c a lca lca( u u u, r o o t root root)!= u u u,那么我们发现在以 r o o t root root为根时 u u u原来的子树就是现在的子树,直接线段树区间查询。

第二种情况: u u u== r o o t root root,那么此时 u u u就是根节点,直接返回线段树根节点的 m i n min min

第三种情况: r o o t root root u u u原来的子树内,即 l c a lca lca( r o o t root root, u u u)== u u u&& u u u!= r o o t root root,怎么做呢?
我们找到 u u u r o o t root root-> u u u这条链上 u u u的儿子 s s s,那么子树 s s s的补集就是我们查询的区间。

没有其他情况了

贴一发代码

#include<bits/stdc++.h>
#define N 100005
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
using namespace std;
inline long long read(){
	long long ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
	return ans;
}
struct Node{int l,r;long long minn,lz;}T[N<<2];
struct node{int v,next;}e[N<<1];
long long a[N];
int first[N],dep[N],top[N],fa[N],hson[N],siz[N],num[N],pred[N],root,n,m,cnt=0,tot=0;
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline void dfs1(int p){
	siz[p]=1,hson[p]=0;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==fa[p])continue;
		fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
inline void dfs2(int p,int tp){
	top[p]=tp,num[p]=++tot,pred[tot]=p;
	if(hson[p])dfs2(hson[p],tp);
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v!=fa[p]&&v!=hson[p])dfs2(v,v);
	}
}
inline long long min(long long a,long long b){return a<b?a:b;}
inline void pushup(int p){T[p].minn=min(T[lc].minn,T[rc].minn);}
inline void pushnow(int p,long long v){T[p].minn=v,T[p].lz=v;}
inline void pushdown(int p){
	if(T[p].lz==0)return;
	pushnow(lc,T[p].lz),pushnow(rc,T[p].lz),T[p].lz=0;
}
inline void build(int p,int l,int r){
	T[p].l=l,T[p].r=r,T[p].lz=0;
	if(l==r){T[p].minn=a[pred[l]];return;}
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(p);
}
inline void update(int p,int ql,int qr,long long v){
	if(T[p].l>qr||T[p].r<ql)return;
	if(ql<=T[p].l&&T[p].r<=qr){pushnow(p,v);return;}
	pushdown(p);
	if(qr<=mid)update(lc,ql,qr,v);
	else if(ql>mid)update(rc,ql,qr,v);
	else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
	pushup(p);
}
inline long long query(int p,int ql,int qr){
	if(T[p].l>qr||T[p].r<ql)return 1e16;
	if(ql<=T[p].l&&T[p].r<=qr)return T[p].minn;
	pushdown(p);
	if(qr<=mid)return query(lc,ql,qr);
	if(ql>mid)return query(rc,ql,qr);
	return min(query(lc,ql,mid),query(rc,mid+1,qr));
}
inline void change(int x,int y,long long v){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		update(1,num[top[x]],num[x],v);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	update(1,num[y],num[x],v);
}
inline int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
inline int fid(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		if(fa[top[x]]==y)return top[x];
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	return hson[y];
}
int main(){
	n=read(),m=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	for(int i=1;i<=n;++i)a[i]=read();
	root=read();
	dfs1(root);
	dfs2(root,root);
	build(1,1,n);
	while(m--){
		int op=read(),u=read();
		if(op==1){root=u;continue;}
		if(op==2){int v=read();long long w=read();change(u,v,w);continue;}
		if(u==root){printf("%lld\n",T[1].minn);continue;}
		int t=lca(u,root);
		if(t!=u){printf("%lld\n",query(1,num[u],num[u]+siz[u]-1));continue;}
		int s=fid(u,root);
		printf("%lld\n",min(query(1,1,num[s]-1),query(1,num[s]+siz[s],n)));
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值