bzoj 3083: 遥远的国度(树上换根操作,树剖+询问整个子树)
3083: 遥远的国度Time Limit: 10 Sec Memory Limit: 1280 MB
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。
题解:
这一题首先肯定要树剖,可是怎样处理询问整个子树的操作呢,其实在树剖过程中得到的 \(p[i]\),其实对于每个结点 \(i\) 的子树, \(p[i]\) 是连续的!
对于换根操作,当然是不能真的换根,我们可以一直以1为根进行参考,对以1为根的树进行剖分后,以 \(i\) 为根的子树中最小的 \(p[i]\) 为 \(in[i]\),最大的 \(p[i]\) 为 \(out[i]\),假如当前树根为 \(cur\),询问 \(u\) 的子树:
(1)若 \(cur==u\) ,则是询问整棵树。
(2)若 \(cur\) 不属于 \(u\) 的子树,那么此时以 \(cur\) 为根的树中 \(u\) 的子树就是以1为根的树中 \(u\) 的子树。
(3)若 \(cur\) 属于 \(u\) 的子树,设 \(t\) 为 \(u\) 的儿子,且在 \(u\) 到 \(cur\) 的路径上
那么画一画就知道,查询范围应该是 \([1,in[t]-1],[out[t]+1,n]\).
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1e5+10;
int v[maxn];
int fa[maxn],top[maxn],son[maxn],num[maxn],p[maxn],dep[maxn],fp[maxn];
//top[v]表示v所在的重链的顶端节点,对于u的轻儿子v有top[v]=v,fa[v]表示v的父亲节点,num[v]表示以v为根的子树的节点数,p[v]表示v与其父亲节点的连边在线段树中的位置,son[v]为v的重儿子,dep为深度
struct node
{
int mi,lazy;
}seg[maxn*4];
void up(int i)
{
seg[i].mi=min(seg[i*2].mi,seg[i*2+1].mi);
}
void pushdown(int i)
{
if(seg[i].lazy)
{
seg[i*2].lazy=seg[i*2].mi=seg[i].lazy;
seg[i*2+1].lazy=seg[i*2+1].mi=seg[i].lazy;
seg[i].lazy=0;
}
}
void build(int i,int l,int r)
{
seg[i].lazy=0;
if(l==r)
{
seg[i].mi=v[fp[l]];
return;
}
int m=(l+r)/2;
build(i*2,l,m),build(i*2+1,m+1,r);
up(i);
}
void update(int i,int l,int r,int L,int R,int v)
{
if(l==L&&r==R)
{
seg[i].lazy=v;
seg[i].mi=v;
return;
}
pushdown(i);
int m=(L+R)/2;
if(r<=m) update(i*2,l,r,L,m,v);
else if(l>m) update(i*2+1,l,r,m+1,R,v);
else
{
update(i*2,l,m,L,m,v);
update(i*2+1,m+1,r,m+1,R,v);
}
up(i);
}
int query(int i,int l,int r,int L,int R)
{
if(l==L&&r==R)
{
return seg[i].mi;
}
if(seg[i].lazy) return seg[i].lazy;
pushdown(i);
int m=(L+R)/2;
if(r<=m) return query(i*2,l,r,L,m);
else if(l>m) return query(i*2+1,l,r,m+1,R);
else
{
return min(query(i*2,l,m,L,m),query(i*2+1,m+1,r,m+1,R));
}
}
int head[maxn];
struct edge
{
int to,next;
}e[maxn*2]; //
int tol=0;
void add(int u,int v)
{
e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
}
int in[maxn],out[maxn];
int pos;//线段树总区间大小
void dfs(int u,int f,int d)
{
num[u]=1;
fa[u]=f;
dep[u]=d;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==f) continue;
dfs(v,u,d+1);
if(son[u]==0||num[v]>num[son[u]])
son[u]=v;
num[u]+=num[v];
}
}
void dfs2(int u,int sp)
{
top[u]=sp;
if(son[u])
{
p[u]=pos;
fp[pos]=u;
in[u]=out[u]=pos;
pos++;
dfs2(son[u],sp);
in[u]=min(in[u],in[son[u]]);
out[u]=max(out[u],out[son[u]]);
}
else //叶子结点
{
p[u]=pos;
fp[pos]=u;
in[u]=out[u]=pos;
pos++;
return;
}
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v!=son[u]&&v!=fa[u])
{
dfs2(v,v);
in[u]=min(in[u],in[v]);
out[u]=max(out[u],out[v]);
}
}
}
void init()
{
pos=1;
memset(fa,0,sizeof(fa));
memset(son,0,sizeof(son));
}
void change(int u,int v,int n,int w)
{
int f1=top[u],f2=top[v];
while(f1!=f2)
{
if(dep[f1]<dep[f2])
{
swap(f1,f2);
swap(u,v);
}
update(1,p[f1],p[u],1,n,w);
u=fa[f1],f1=top[u];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,p[u],p[v],1,n,w);
}
int f1[maxn][18];
void bfs(int rt)
{
queue<int> q;
dep[rt] = 1;
f1[rt][0] = rt;
q.push(rt);
while(!q.empty())
{
int t = q.front();
q.pop();
for(int i = 1 ; i <= 17 ; i++)
f1[t][i] = f1[f1[t][i-1]][i-1];
for(int i = head[t] ; i ; i = e[i].next)
{
int v = e[i].to;
if(v == f1[t][0])continue;
dep[v] = dep[t]+1;
f1[v][0] = t;
q.push(v);
}
}
}
int get_up(int u,int k)
{
int tu=u;
for(int det = k, i = 0; det ;det>>=1, i++)
if(det&1)
tu = f1[tu][i];
return tu;
}
int main()
{
init();
int n,m;
scanf("%d%d",&n,&m);
rep(i,1,n+1) in[i]=1e9,out[i]=0;
rep(i,1,n)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
rep(i,1,n+1) scanf("%d",&v[i]);
int cur;
scanf("%d",&cur);
bfs(1);
dfs(1,0,1);
dfs2(1,1);
build(1,1,n);
while(m--)
{
int op,u,v,w;
scanf("%d",&op);
if(op==1)
{
scanf("%d",&cur);
}
else if(op==2)
{
scanf("%d%d%d",&u,&v,&w);
change(u,v,n,w);
}
else if(op==3)
{
scanf("%d",&u);
int ans=1e9;
if(u==cur)
ans=seg[1].mi;
else if(in[u]<=in[cur]&&out[cur]<=out[u])
{
int k=dep[cur]-dep[u]-1;
int tv=get_up(cur,k);
if(in[tv]>1)
ans=min(ans,query(1,1,in[tv]-1,1,n));
if(out[tv]<n)
ans=min(ans,query(1,out[tv]+1,n,1,n));
}
else
ans=query(1,in[u],out[u],1,n);
printf("%d\n",ans);
}
}
return 0;
}