BZOJ_3083_遥远的国度_树链剖分+线段树
BZOJ_3083_遥远的国度_树链剖分
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
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
总不能每次换根都对整个树剖一遍,因此我们要先以1为根进行树剖,然后对根的位置进行分类讨论。
假设询问的时x,根为root。
当root=x时直接输出整个树的最小值,这个必须要判而不能放到其他分类中。
当root在x的子树外时,由于x的子树没有变,相当于还是查询x的子树。
当root在x的子树内时,答案由两部分组成:
1.x子树外,这个对应树剖序上两段区间。
2.x子树内,需要刨去x到root路径上的点,我们对root不断跳重链,直到跳到x的儿子上,这个点的子树需要刨去,剩下的也是两段区间。
分类讨论完成后直接树剖+线段树即可。
注意权值可能爆int,需要用unsigned int
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 100050 #define ls p<<1 #define rs p<<1|1 #define inf ((1u<<31)+1) typedef unsigned int un; int head[N],to[N<<1],nxt[N<<1],n,cnt,m; int son[N],fa[N],dep[N],siz[N],top[N],idx[N],root; un t[N<<2],tag[N<<2],val[N],a[N]; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } void dfs1(int x,int y) { int i; fa[x]=y; dep[x]=dep[y]+1; siz[x]=1; for(i=head[x];i;i=nxt[i]) { if(to[i]!=y) { dfs1(to[i],x); siz[x]+=siz[to[i]]; if(siz[to[i]]>siz[son[x]]) son[x]=to[i]; } } } void dfs2(int x,int t) { top[x]=t; idx[x]=++idx[0]; if(son[x]) dfs2(son[x],t); int i; for(i=head[x];i;i=nxt[i]) { if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]); } } void build(int l,int r,int p) { if(l==r) { t[p]=a[l]; return ; } int mid=(l+r)>>1; build(l,mid,ls); build(mid+1,r,rs); t[p]=min(t[ls],t[rs]); } inline void pushdown(int p) { if(tag[p]) { t[ls]=tag[ls]=tag[p]; t[rs]=tag[rs]=tag[p]; tag[p]=0; } } void update(int l,int r,int x,int y,un v,int p) { if(x<=l&&y>=r) { t[p]=v; tag[p]=v; return ; } pushdown(p); int mid=(l+r)>>1; if(x<=mid) update(l,mid,x,y,v,ls); if(y>mid) update(mid+1,r,x,y,v,rs); t[p]=min(t[ls],t[rs]); } un qmin(int l,int r,int x,int y,int p) { if(x<=l&&y>=r) return t[p]; pushdown(p); int mid=(l+r)>>1; un re=inf; if(x<=mid) re=min(re,qmin(l,mid,x,y,ls)); if(y>mid) re=min(re,qmin(mid+1,r,x,y,rs)); return re; } un solve(int x,int y) { un re=inf; if(idx[x]<=idx[y]-1) re=min(re,qmin(1,n,idx[x],idx[y]-1,1)); if(idx[y]+siz[y]<=idx[x]+siz[x]-1) re=min(re,qmin(1,n,idx[y]+siz[y],idx[x]+siz[x]-1,1)); return re; } int main() { scanf("%d%d",&n,&m); int i,x,y,opt; un z; for(i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(i=1;i<=n;i++) scanf("%u",&i[val]); scanf("%d",&root); dfs1(1,0); dfs2(1,1); for(i=1;i<=n;i++) a[idx[i]]=val[i]; build(1,n,1); while(m--) { scanf("%d%d",&opt,&x); if(opt==1) { root=x; }else if(opt==2) { scanf("%d%u",&y,&z); while(top[x]!=top[y]) { if(dep[top[x]]>dep[top[y]]) swap(x,y); update(1,n,idx[top[y]],idx[y],z,1); y=fa[top[y]]; } if(dep[x]<dep[y]) swap(x,y); update(1,n,idx[y],idx[x],z,1); }else { if(root==x) { printf("%d\n",t[1]); }else if(idx[root]<idx[x]||idx[root]>idx[x]+siz[x]-1) { printf("%u\n",qmin(1,n,idx[x],idx[x]+siz[x]-1,1)); }else { un re=inf; if(1<=idx[x]-1) re=min(re,qmin(1,n,1,idx[x]-1,1)); if(idx[x]+siz[x]<=n) re=min(re,qmin(1,n,idx[x]+siz[x],n,1)); int y=root; if(fa[y]==x) { re=min(re,solve(x,y)); }else { if(top[y]==top[x]) { re=min(re,solve(x,son[x])); }else { while(fa[y]!=x) { y=fa[y]; if(fa[y]==x) break; if(top[x]==top[y]) break; y=top[y]; } while(fa[y]!=x) y=fa[y]; re=min(re,solve(x,y)); } } printf("%u\n",re); } } } }