BZOJ 3083: 遥远的国度 树链剖分 线段树 换根分类讨论

3083: 遥远的国度

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 4587  Solved: 1255
[Submit][Status][Discuss]

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。

若 u==root

查询dfn在[1,n]的点

若 u在root的子树内

查询以选定假根为根内的子树

若 u不在root到选定假根的链上

查询以选定假根为根内的子树

若 u在root到选定假根的链上

发现现在u的子树就是整棵树减去u往root方向向下那个节点的子树

于是倍增求那个点然后整个dfs序就是分成两段了啊


#include<cmath>
#include<ctime>
#include<cstdio>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef double db;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=100100;

int last[N],ecnt;
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

int n,m,root,V[N];

int top[N],dfn[N],size[N],pos[N],tim;
int dep[N],anc[N][20];

inline int get_lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	int len=dep[u]-dep[v];
	for(int i=0;(1<<i)<=len;++i)
		if((1<<i)&len)
			u=anc[u][i];
	if(u==v) return u;
	for(int i=17;~i;--i)
		if(anc[u][i]!=anc[v][i])
			u=anc[u][i],v=anc[v][i];
	return anc[u][0];
}

inline int jump(int u,int aim)
{
	int len=dep[u]-aim;
	for(int i=0;(1<<i)<=len;++i)
		if((1<<i)&len)
			u=anc[u][i];
	return u;
}

namespace decomposition
{
	int son[N];
	
	void dfs_1(int u,int fa)
	{
		size[u]=1;
		for(int i=1;(1<<i)<=dep[u];++i)
			anc[u][i]=anc[anc[u][i-1]][i-1];
		for(int i=last[u],v;i;i=e[i].nt)
			if(fa!=(v=e[i].to))
			{
				dep[v]=dep[u]+1,
				anc[v][0]=u,
				dfs_1(v,u),
				size[u]+=size[v];
				if(size[v]>size[son[u]]) son[u]=v;
			}
	}
	
	void dfs_2(int u,int fa)
	{
		if(!top[u]) top[u]=u;
		dfn[u]=++tim,pos[tim]=u;
		if(son[u])
			top[son[u]]=top[u],dfs_2(son[u],u);
		for(int i=last[u],v;i;i=e[i].nt)
			if((v=e[i].to)!=fa && v!=son[u])
				dfs_2(v,u);
	}
	
	void Main()
	{
		dfs_1(1,0),
		dfs_2(1,0);
	}
}

int mn[N<<2],tag[N<<2];

inline void pushdown(int k)
{
	if(tag[k])
	{
		mn[k<<1]=mn[k<<1|1]=
		tag[k<<1]=tag[k<<1|1]=tag[k];
		tag[k]=0;
	}
}

inline void pushup(int k)
{mn[k]=min(mn[k<<1],mn[k<<1|1]);}

void build(int k,int l,int r)
{
	if(l==r){mn[k]=V[pos[l]];return ;}
	int mid=(l+r)>>1;
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
	pushup(k);
}

void modify(int k,int l,int r,int x,int y,int val)
{
	if(l>=x && r<=y)
	{
		tag[k]=mn[k]=val;
		return ;
	}
	int mid=(l+r)>>1;pushdown(k);
	if(x<=mid) modify(k<<1,l,mid,x,y,val);
	if(y>mid) modify(k<<1|1,mid+1,r,x,y,val);
	pushup(k);
}

int query(int k,int l,int r,int x,int y)
{
	if(x>y) return INT_MAX;
	if(l>=x && r<=y) return mn[k];
	int mid=(l+r)>>1;pushdown(k);
	if(y<=mid) return query(k<<1,l,mid,x,y);
	if(x>mid) return query(k<<1|1,mid+1,r,x,y);
	return min(query(k<<1,l,mid,x,y),query(k<<1|1,mid+1,r,x,y));
}

void get_modify(int u,int v,int val)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		modify(1,1,n,dfn[top[u]],dfn[u],val);
		u=anc[top[u]][0];
	}
	if(dep[u]<dep[v]) swap(u,v);
	modify(1,1,n,dfn[v],dfn[u],val);
}

int get_query(int u,int v,int val)
{
	int res(INT_MAX);
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		res=min(res,query(1,1,n,dfn[top[u]],dfn[u]));
		u=anc[top[u]][0];
	}
	if(dep[u]<dep[v]) swap(u,v);
	res=min(res,query(1,1,n,dfn[v],dfn[u]));
	return res;
}

int get_ans(int u)
{
	if(u==root) return query(1,1,n,1,n);
	if(dfn[u]>=dfn[root] && dfn[u]<=dfn[root]+size[root]-1)
		return query(1,1,n,dfn[u],dfn[u]+size[u]-1);
	int lca=get_lca(u,root);
	if(lca==u)
	{
		u=jump(root,dep[u]+1);
		return min(query(1,1,n,1,dfn[u]-1),query(1,1,n,dfn[u]+size[u],n));
	}
	else return query(1,1,n,dfn[u],dfn[u]+size[u]-1);
}

int main()
{
	n=read(),m=read();
	register int i,u,v,opt,val;
	for(i=1;i<n;++i)
		u=read(),v=read(),
		add(u,v),add(v,u);
	for(i=1;i<=n;++i) V[i]=read();
	
	decomposition::Main();
	build(1,1,n);
	root=read();
	
	while(m--)
	{
		opt=read();
		switch(opt)
		{
			case 1:
				root=read();
				break;
			case 2:
				u=read(),v=read(),val=read();
				get_modify(u,v,val);
				break;
			case 3:
				u=read();
				print(get_ans(u));puts("");
				break;
		}
	}
	return 0;
}

查询以选定假根为根内的子树

猜你喜欢

转载自blog.csdn.net/BlackJack_/article/details/80580948
今日推荐