BZOJ3083 遥远的国度

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

Hint

【数据范围】
  对于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。

%%%huangziyue大佬%%%

网上树剖题解很不优美,要讨论3种情况,于是我很不负责任的贴一份lct解法的,因为会re一组至今无解o((⊙﹏⊙))o

lct维护子树信息——维护最小值,Tarjan似乎没想过要用lct维护子树信息,不过参照大融合,我们照样可以维护子树最值。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+7;
struct Splay{
	int c[N];
	bool rev[N];
	int vl[N],Min[N];
	multiset<int>s[N];//记录虚儿子 
	int p[N],ch[N][2];
	int tmin[N],fmin[N];//tmin维护实儿子,fmin维护虚儿子 
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	inline bool isroot(int x){
		return (Ls(p[x])^x)&&(rs(p[x])^x);
	}
	inline void pushdown(int x){
		if(c[x]){
			c[Ls(x)]=c[rs(x)]=c[x];
			vl[x]=c[x];
			c[x]=0;
		}
		if(rev[x]){
			rev[Ls(x)]^=1,rev[rs(x)]^=1;
			swap(Ls(x),rs(x));
			rev[x]=0;
		}
	}
	inline void maintain(int x){
		tmin[x]=vl[x],fmin[x]=*(s[x].begin());
		if(Ls(x))tmin[x]=min(tmin[x],tmin[Ls(x)]),fmin[x]=min(fmin[x],fmin[Ls(x)]);
		if(rs(x))tmin[x]=min(tmin[x],tmin[rs(x)]),fmin[x]=min(fmin[x],fmin[rs(x)]);
		Min[x]=min(tmin[x],fmin[x]);
	}
	inline void rot(int x){
		int f=p[x],gf=p[f],type=rs(f)==x,son=ch[x][type^1];
		if(isroot(f)^1)ch[gf][rs(gf)==f]=x;p[x]=gf;
		ch[p[son]=f][type]=son,maintain(f);
		ch[p[f]=x][type^1]=f,maintain(x); 
	}
	int top,stk[N];
	inline void splay(int x){
		stk[++top]=x;
		if(isroot(x)^1)for(int i=x;isroot(i)^1;i=p[i])stk[++top]=p[i];
		while(top)pushdown(stk[top--]);
		while(isroot(x)^1){
			if(isroot(p[x]))return rot(x),void();
			if((rs(p[p[x]])==p[x])==(rs(p[x])==x))rot(p[x]);
			rot(x);
		}
	}
	inline void init(int x,int v,int fa){
		s[x].insert(fmin[x]=1<<30);
		vl[x]=Min[x]=tmin[x]=v;
		rev[x]=c[x]=0;
		p[x]=fa;
	}
};
struct LCT{
	Splay sp;
	inline void access(int x){
		for(int lastx=0;x;lastx=x,x=sp.p[x]){
			sp.splay(x);
			if(sp.rs(x))sp.s[x].insert(sp.Min[sp.rs(x)]);
			sp.rs(x)=lastx;
			if(sp.rs(x))sp.s[x].erase(sp.s[x].find(sp.Min[sp.rs(x)]));
			sp.maintain(x);
		}
	}
	inline void makeroot(int x){
		access(x),sp.splay(x),sp.rev[x]^=1;
	}
	inline void split(int x,int y){
		makeroot(x),access(y),sp.splay(y);
	}
}lct;
int n,m,Root,vl[N];
struct Edge{
	int cnt,h[N],to[N<<1],next[N<<1];
	inline void add(int x,int y){
		next[++cnt]=h[x];
		to[cnt]=y;
		h[x]=cnt;
	}
}e;
void dfs(int x,int fa){//害怕撑爆 
	lct.sp.init(x,vl[x],fa);
	for(int p=e.h[x];p;p=e.next[p])if(e.to[p]^fa){
		dfs(e.to[p],x);
		lct.sp.s[x].insert(lct.sp.Min[e.to[p]]);
	}
	lct.sp.maintain(x);
}
inline void init(){
	scanf("%d%d",&n,&m);
	Inc(i,1,n-1){
		int x,y;scanf("%d%d",&x,&y);
		e.add(x,y),e.add(y,x);
	}
	Inc(i,1,n)scanf("%d",&vl[i]);
	scanf("%d",&Root);
}
inline void solv(){
	lct.makeroot(Root);
	while(m--){
		int op,x,y,z;scanf("%d",&op);
		if(op==1)scanf("%d",&Root),lct.makeroot(Root);
		else if(op==2){
			scanf("%d%d%d",&x,&y,&z);
			lct.split(x,y);
			lct.sp.c[y]=z;
		}else {
			scanf("%d",&x);
			lct.makeroot(Root),lct.access(x),lct.sp.splay(x);//实子树全为祖先 
			cout<<min(lct.sp.vl[x],*(lct.sp.s[x].begin()))<<"\n";
		}
	}
}
int main(){
	init();
	dfs(Root,0);
	solv();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值