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

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


总不能每次换根都对整个树剖一遍,因此我们要先以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);
}
}
}
}
上一篇:【架构师之路】Nginx负载均衡与反向代理—《亿级流量网站架构核心技术》


下一篇:SharePoint表单和工作流 - Nintex篇(四)