BZOJ 3083 遥远的国度
这题的与 HAOI 2015T2 的不同点在于其有换根操作,但按照普通的思路我们可以发现,换根之后树的形态会有改变,每个节点的子树会发生改变,所以我们来分类讨论。
修改链的操作不会发生改变,现在只考虑子树minn。为了方便,我们定义现在的换的“根节点”为root(但实际上树的根节点为1),子树根为x,黑圈标明查询范围;
情况一 x=root,很显然此时应当查询整棵树。
情况二 lca(root,x)!=x ,此时直接查询x的子树即可,与换根无关。
情况三,lca(root,x)=x,此时我们应当查询与x相邻的节点中与root最近的点v在整棵树中的补集
可以发现v一定在root到x的链上,且一定是x在这条链上的儿子,倍增法可以求得v
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
#define inf 2147483647
using namespace std;
struct hp{
int fat,dep,size,wson,top,ls,rs;
int lca[18];
}tree[100001];
struct hq{
int u,v;
}a[200001];
struct hr{
int minn,delta;
}seg[400001];
int point[100001],next[100001];
int val[400001],plc[100001];
int n,m,ans,e=1,totw=0,root;
void add(int x,int y)
{
e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e;
e++; a[e].u=y; a[e].v=x; next[e]=point[y]; point[y]=e;
}
void build_tree(int last,int x,int depth)
{
int i;
tree[x].lca[0]=tree[x].fat=last;
tree[x].size=1;
tree[x].wson=0;
tree[x].dep=depth;
for (i=1;i<=17;++i)
{
if (tree[x].dep >= 1<<i)
tree[x].lca[i]=tree[tree[x].lca[i-1]].lca[i-1];
}
for (i=point[x];i;i=next[i])
if (a[i].v!=last)
{
build_tree(x,a[i].v,depth+1);
if (tree[tree[x].wson].size<tree[a[i].v].size)
tree[x].wson=a[i].v;
}
}
void build_seg(int now,int tp)
{
int i;
tree[now].top=tp;
tree[now].ls=plc[now]=++totw;
if (tree[now].wson!=0)
build_seg(tree[now].wson,tp);
for (i=point[now];i;i=next[i])
if (a[i].v!=tree[now].fat&&a[i].v!=tree[now].wson)
build_seg(a[i].v,a[i].v);
tree[now].rs=totw;
}
void updata(int i)
{
seg[i].minn=min(seg[i<<1].minn,seg[i<<1|1].minn);
}
void paint(int i,int a)
{
seg[i].minn=a;
seg[i].delta=a;
}
void pushdown(int i)
{
paint(i<<1,seg[i].delta);
paint(i<<1|1,seg[i].delta);
seg[i].delta=0;
}
void build(int i,int l,int r)
{
if (l==r)
{
seg[i].minn=val[l];
return;
}
build(lch); build(rch);
updata(i);
}
void insert_seg(int i,int l,int r,int x,int y,int a)
{
if (x<=l&&y>=r)
{
paint(i,a);
return;
}
if (seg[i].delta!=0)
pushdown(i);
if (x<=mid) insert_seg(lch,x,y,a);
if (y>mid) insert_seg(rch,x,y,a);
updata(i);
}
void insert(int x,int y,int a)
{
int f1=tree[x].top,f2=tree[y].top;
while (f1!=f2)
{
if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);}
insert_seg(1,1,n,plc[f1],plc[x],a);
x=tree[f1].fat; f1=tree[x].top;
}
if (tree[x].dep>tree[y].dep) swap(x,y);
insert_seg(1,1,n,plc[x],plc[y],a);
}
int bz(int x,int depth)
{
int i,v=x;
for (i=0;i<=17;++i)
{
if (depth&(1<<i))
v=tree[v].lca[i];
}
return v;
}
void query_seg(int i,int l,int r,int x,int y)
{
if (x<=l&&y>=r)
{
ans=min(ans,seg[i].minn);
return;
}
if (seg[i].delta!=0)
pushdown(i);
if (x<=mid) query_seg(lch,x,y);
if (y>mid) query_seg(rch,x,y);
}
int LCA(int x,int y)
{
int i,t;
if (tree[x].dep<tree[y].dep) swap(x,y);
t=tree[x].dep-tree[y].dep;
for (i=0;i<=17;++i)
{
if (t&(1<<i))
x=tree[x].lca[i];
}
for (i=17;i>=0;--i)
if (tree[x].lca[i]!=tree[y].lca[i])
{x=tree[x].lca[i]; y=tree[y].lca[i];}
if (x==y) return x;
else return tree[x].fat;
}
void work(int x)
{
int lca,v,rootx=root,xx=x;
lca=LCA(root,x);
if (rootx==xx)//情况1
printf("%d\n",seg[1].minn);
else
{
if (lca!=xx)//情况2
query_seg(1,1,n,tree[xx].ls,tree[xx].rs);
else//情况三
{
v=bz(rootx,tree[rootx].dep-tree[xx].dep-1);
if (tree[v].ls>1)
query_seg(1,1,n,1,tree[v].ls-1);
if (tree[v].rs<n)
query_seg(1,1,n,tree[v].rs+1,n);
}
printf("%d\n",ans);
}
root=rootx;
}
int main()
{
int i,x,y,opt,a;
scanf("%d%d",&n,&m);
for (i=1;i<=n-1;++i)
{
scanf("%d%d",&x,&y);
add(x,y);
}
build_tree(0,1,0);
build_seg(1,1);
for (i=1;i<=n;++i)
scanf("%d",&val[plc[i]]);
build(1,1,n);
scanf("%d",&root);
for (i=1;i<=m;++i)
{
scanf("%d",&opt);
if (opt==1)
{
scanf("%d",&x);
root=x;
}
if (opt==2)
{
scanf("%d%d%d",&x,&y,&a);
insert(x,y,a);
}
if (opt==3)
{
scanf("%d",&x);
ans=inf;
work(x);
}
}
}