3083: 遥远的国度
Time Limit: 10 Sec Memory Limit: 1280 MB
Submit: 2165 Solved: 525
[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提供
题解:
很明显的树链剖分
至于换根操作,不同于动态树问题中的换根,动态树问题的换根中,树的形态和结构发生了变化,所以需要用灵活的LCT之类的去维护,而这个题,仔细分析发现,只是名义上的根发生了变化,实际树的形态和结构并没有发生变化,所以还是可以用链剖去解决,只需要分类讨论一下即可;
操作1:记录下新的根即可
操作2:随便搞搞,求一下区间的最小即可,同T2
操作3:根据id和root分类讨论;
①root==id,询问整棵树
②fa[root]==id,询问除了root所在子树以外的整棵树
③root在id的子树里,且距离大于1,询问除了root的除了其祖先是id的儿子的祖先的子树以外的整棵树
④root不在id的子树里,询问id的子树
code:
#include
#include
#include
#include
#include
using namespace std;
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;
}
#define maxn 200010
int n,m,sd;struct data{int next,to;}edge[maxn*2];
int head[maxn],cnt;
void add(int u,int v){cnt++;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].to=v;}
void insert(int u,int v){add(u,v);add(v,u);}
//--------------------------------------------------------------------------------------
int deep[maxn],size[maxn],son[maxn],pl[maxn],sz,pr[maxn],pre[maxn],top[maxn],fa[maxn],father[maxn][25];
void dfs_1(int now)
{
size[now]=1;
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to!=fa[now])
{
fa[edge[i].to]=now;
deep[edge[i].to]=deep[now]+1;
dfs_1(edge[i].to);
if (size[son[now]].to]) son[now]=edge[i].to;
size[now]+=size[edge[i].to];
}
}
void dfs_2(int now,int chain)
{
pl[now]=++sz; pre[sz]=now; top[now]=chain;
if (son[now]) dfs_2(son[now],chain);
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to!=son[now] && edge[i].to!=fa[now])
dfs_2(edge[i].to,edge[i].to);
pr[now]=sz;
}
int LCA(int x,int y)
{
int dx=deep[x],dy=deep[y];
if (dxint dd=dx-dy;
for (int i=0; i<=20; i++) if (dd&(1<for (int i=20; i>=1; i++)
if (father[x][i]!=father[y][i])
x=father[x][i],y=father[y][i];
if (x==y) return x; else return father[x][0];
}
//--------------------------------------------------------------------------------------
int tree[maxn<<2],del[maxn<<2];
inline void update(int now)
{if (tree[now<<1]1|1]) tree[now]=tree[now<<1];else tree[now]=tree[now<<1|1];}
inline void pushdown(int now,int l,int r)
{
if (!del[now]) return;
int mid=(l+r)>>1; int dd=del[now];
del[now]=0; del[now<<1]=dd; del[now<<1|1]=dd;
tree[now<<1]=dd; tree[now<<1|1]=dd;
}
void point_change(int now,int l,int r,int loc,int val)
{
pushdown(now,l,r);
if (l==r) {tree[now]+=val;return;}
int mid=(l+r)>>1;
if (loc<=mid) point_change(now<<1,l,mid,loc,val);
else point_change(now<<1|1,mid+1,r,loc,val);
update(now);
}
void segment_change(int now,int l,int r,int L,int R,int val)
{
pushdown(now,l,r);
if (L<=l && R>=r) {tree[now]=val;del[now]=val;return;}
int mid=(l+r)>>1;
if (L<=mid) segment_change(now<<1,l,mid,L,R,val);
if (R>mid) segment_change(now<<1|1,mid+1,r,L,R,val);
update(now);
}
int segment_ask(int now,int l,int r,int L,int R)
{
pushdown(now,l,r);
if (L<=l && R>=r) return tree[now];
int mid=(l+r)>>1; int ans=0x7fffffff;
if (L<=mid) ans=min(ans,segment_ask(now<<1,l,mid,L,R));
if (R>mid) ans=min(ans,segment_ask(now<<1|1,mid+1,r,L,R));
return ans;
}
//--------------------------------------------------------------------------------------
void solve1(int id)
{
sd=id;
}
void solve2(int p1,int p2,int val)
{
while (top[p1]!=top[p2])
{
int tp1=top[p1],tp2=top[p2];
if (deep[tp1]1,1,n,pl[tp1],pl[p1],val);
p1=fa[p1]; tp1=top[p1];
}
if (deep[p1]>deep[p2]) swap(p1,p2);
segment_change(1,1,n,pl[p1],pl[p2],val);
}
void solve3(int id)
{
if(id==sd) printf("%d\n",segment_ask(1,1,n,1,n));
else if(fa[sd]==id)
printf("%d\n",min(segment_ask(1,1,n,1,pl[sd]-1),pr[sd]==n?0x7fffffff:segment_ask(1,1,n,pr[sd]+1,n)));
else if(pl[sd]>=pl[id]&&pl[sd]<=pr[id])
{
int U=sd;
while(fa[top[U]]!=id&&top[U]!=top[id]) U=fa[top[U]];
if(fa[top[U]]!=id)
U=pre[pl[id]+1];
else
U=top[U];
printf("%d\n",min(segment_ask(1,1,n,1,pl[U]-1),pr[U]==n?0x7fffffff:segment_ask(1,1,n,pr[U]+1,n)));
}
else
printf("%d\n",segment_ask(1,1,n,pl[id],pr[id]));
}
//--------------------------------------------------------------------------------------
int main()
{
n=read(),m=read();
for (int u,v,i=1; i<=n-1; i++)
u=read(),v=read(),insert(u,v);
dfs_1(1); dfs_2(1,1);
for (int pro,i=1; i<=n; i++)
pro=read(),point_change(1,1,n,pl[i],pro);
sd=read();
for (int i=1; i<=m; i++)
{
int opt=read();int id,p1,p2,v;
switch (opt)
{
case 1: id=read(); solve1(id);break;
case 2: p1=read(),p2=read(),v=read(); solve2(p1,p2,v);break;
case 3: id=read(); solve3(id);break;
}
}
return 0;
}