bzoj3083 遥远的国度

3083: 遥远的国度

Time Limit: 10 Sec   Memory Limit: 1280 MB
Submit: 1960   Solved: 484
[ 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。

HINT

Source

zhonghaoxi提供




要求维护链上修改、子树查询和换根操作。

树链剖分本质就是一种DFS序,所以子树查询可以转化为区间查询,用线段树维护即可。

而换根操作只需要记录当前根节点的序号,每次查询时判断根和查询节点的位置关系即可。(详见bzoj3306 树)

这道题调了一下午,WA了无数次,原因是代码第149行t的赋值写成了t=d[rt]-d[x]……




#include
#include
#include
#include
#include
#include
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100005
#define inf 1000000000
using namespace std;
struct edge_type
{
	int next,to;
}e[maxn*2];
struct seg
{
	int l,r,mn,tag;
}t[maxn*4];
int n,m,cnt,tot,x,y,z,rt,opt;
int head[maxn],sz[maxn],w[maxn],d[maxn],son[maxn];
int belong[maxn],f[maxn],fa[maxn][20],l[maxn],r[maxn];
bool vst[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y)
{
	e[++cnt]=(edge_type){head[x],y};head[x]=cnt;
	e[++cnt]=(edge_type){head[y],x};head[y]=cnt;
}
inline void dfs1(int x)
{
	sz[x]=1;
	F(i,1,18)
	{
		if ((1<sz[son[x]]) son[x]=y;
	}
}
inline void dfs2(int x,int chain)
{
	l[x]=++tot;f[tot]=x;belong[x]=chain;
	if (son[x]) dfs2(son[x],chain);
	for(int i=head[x];i;i=e[i].next)
		if (!l[e[i].to]&&son[x]!=e[i].to) dfs2(e[i].to,e[i].to);
	r[x]=tot;
}
inline int lca(int x,int y)
{
	if (d[x]>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	pushup(k);
}
inline void change(int k,int l,int r,int x)
{
	if (t[k].l==l&&t[k].r==r){update(k,x);return;}
	int mid=(t[k].l+t[k].r)>>1;
	pushdown(k);
	if (r<=mid) change(k<<1,l,r,x);
	else if (l>mid) change(k<<1|1,l,r,x);
	else {change(k<<1,l,mid,x);change(k<<1|1,mid+1,r,x);}
	pushup(k);
}
inline int query(int k,int l,int r)
{
	if (t[k].l==l&&t[k].r==r) return t[k].mn;
	int mid=(t[k].l+t[k].r)>>1;
	pushdown(k);
	if (r<=mid) return query(k<<1,l,r);
	else if (l>mid) return query(k<<1|1,l,r);
	else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
inline void solvecover(int x,int f,int z)
{
	while (belong[x]!=belong[f])
	{
		change(1,l[belong[x]],l[x],z);
		x=fa[belong[x]][0];
	}
	change(1,l[f],l[x],z);
}
int main()
{
	n=read();m=read();
	F(i,1,n-1){x=read();y=read();add_edge(x,y);}
	F(i,1,n) w[i]=read();
	rt=read();
	dfs1(rt);dfs2(rt,rt);
	build(1,1,n);
	F(i,1,m)
	{
		opt=read();
		if (opt==1) rt=read();
		else if (opt==2)
		{
			x=read();y=read();z=read();
			int f=lca(x,y);
			solvecover(x,f,z);solvecover(y,f,z);
		}
		else
		{
			x=read();
			if (x==rt) printf("%d\n",t[1].mn);
			else
			{
				int f=lca(x,rt);
				if (x==f)
				{
					int t=d[rt]-d[x]-1,ans=inf;y=rt;
					F(i,0,19) if ((1<


你可能感兴趣的:(线段树,树链剖分,OIer的狂欢)