洛谷3979 BZOJ3083 遙遠的國度 樹剖 倍增LCA
阿新 • • 發佈:2018-11-23
題意:
給你一棵樹,要支援下列操作:1.鏈賦值2.子樹min3.換根,要nlogn
題解:
這題看了幾個題解完全沒有說換根是怎麼維護的,都在說看程式碼,一氣之下就自己想出了這個題。
鏈賦值和子樹min很容易想到樹剖,但是樹剖不支援換根。我太弱不會top tree,只能想辦法解決換根問題。
我們考慮換根對答案的影響,我們畫個圖應該不難看出換根會影響的只有新的根到原來根的路徑上的點。那麼詢問其他點的話答案不變。我們考慮新根到原來根的路徑上的點,答案的變化是去掉新根所在的子樹,加上原來父節點變成新的子樹。我們不能重新dfs,所以我們要想辦法用原來的dfs序求出新的答案。我們可以分類討論來求出答案。如果詢問的點是當前根,我們可以直接用整棵樹的min。我們設詢問的點是x,那麼如果x與當前根的lca不是x本身,那麼就意味著新的根不在x的子樹中,那麼要不然x在當前根的子樹,要麼x和當前根不在初始根的同一個子樹中。對於這種情況,答案沒有和初始情況變化。要是x與當前根的lca是x本身,那麼意味著x在原始根和現在的根之間,那麼當前詢問的答案應該是在原始樹中除去當前根所在的x的子樹之後其他所有點的min,於是我們需要通過倍增和樹剖時算出的深度求出根rt向上 次之後的祖先,也就是x對應的向當前根方向走一步的點,設為z。我們其實相當於用所有的點去掉以原樹中以z為根的子樹,在dfs序中就是 和 。這樣我們就可以用原始的dfs序來維護換根了。
程式碼:
#include <bits/stdc++.h>
using namespace std;
int n,m,hed[100010],cnt,rt,b[100010],fa[100010],dep[100010],sz[100010],son[100010];
int ed[100010],ys[100010],yss[100010],z,tp[100010];
int f[100010][20];
struct node
{
int to,next;
}a[200010];
struct tree
{
int l,r,mn,tag;
}tr[400010];
inline void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=hed[from];
hed[from]=cnt;
}
inline void dfs(int x)
{
sz[x]=1;
for(int i=1;i<=19;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y!=fa[x])
{
dep[y]=dep[x]+1;
fa[y]=x;
f[y][0]=x;
dfs(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])
son[x]=y;
}
}
}
inline void dfs2(int x,int top)
{
ys[x]=++z;
yss[z]=x;
tp[x]=top;
if(son[x])
{
dfs2(son[x],top);
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
}
ed[x]=z;
}
inline void pushup(int rt)
{
tr[rt].mn=min(tr[rt<<1].mn,tr[rt<<1|1].mn);
}
inline void build(int rt,int l,int r)
{
tr[rt].l=l;
tr[rt].r=r;
if(l==r)
{
tr[rt].mn=b[yss[l]];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline void pushdown(int rt)
{
if(tr[rt].tag)
{
tr[rt<<1].mn=tr[rt].tag;
tr[rt<<1|1].mn=tr[rt].tag;
tr[rt<<1].tag=tr[rt].tag;
tr[rt<<1|1].tag=tr[rt].tag;
tr[rt].tag=0;
}
}
inline void update(int rt,int le,int ri,int z)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
{
tr[rt].mn=z;
tr[rt].tag=z;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(le<=mid)
update(rt<<1,le,ri,z);
if(mid+1<=ri)
update(rt<<1|1,le,ri,z);
pushup(rt);
}
inline void change(int x,int y,int z)
{
int fx=tp[x],fy=tp[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy])
{
swap(x,y);
swap(fx,fy);
}
update(1,ys[fx],ys[x],z);
x=fa[fx];
fx=tp[x];
}
if(dep[x]<dep[y])
update(1,ys[x],ys[y],z);
else
update(1,ys[y],ys[x],z);
}
inline int query(int rt,int le,int ri)
{
int l=tr[rt].l,r=tr[rt].r;
if(l>ri||r<le)
return 0;
if(le<=l&&r<=ri)
return tr[rt].mn;
pushdown(rt);
int mid=(l+r)>>1,ans=2147483647;
if(le<=mid)
ans=min(ans,query(rt<<1,le,ri));
if(mid+1<=ri)
ans=min(ans,query(rt<<1|1,le,ri));
return ans;
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=19;i>=0;--i)
{
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
}
if(x==y)
return x;
for(int i=19;i>=0;--i)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;++i)
scanf("%d",&b[i]);
scanf("%d",&rt);
dep[rt]=1;
dfs(rt);
dfs2(rt,rt);
build(1,1,n);
for(int i=1;i<=m;++i)
{
int opt;
scanf("%d",&opt);
if(opt==1)
{
int x;
scanf("%d",&x);
rt=x;
}
if(opt==2)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
change(x,y,z);
}
if(opt==3)
{
int x;
scanf("%d",&x);
if(x==rt)
printf("%d\n",query(1,1,n));
else
{
int z=lca(x,rt);
if(z!=x)
printf("%d\n",query(1,ys[x],ed[x]));
else
{
int qaq=dep[rt]-dep[x]-1;
z=rt;
for(int i=19;i>=0;--i)
{
if((1<<i)<=qaq)
{
z=f[z][i];
qaq-=(1<<i);
}
}
int ans=2147483647;
if(ys[z]>1)
ans=query(1,1,ys[z]-1);
if(ed[z]+1<=n)
ans=min(ans,query(1,ed[z]+1,n));
printf("%d\n",ans);
}
}
}
}
return 0;
}