题解[P3979遥远的国度]
题目链接
题意:1. 换根 2. 路径覆盖 3. 查子树最小值
看到这样的题,显然一个 \(Top\) \(Tree\) 直接没了,
但我还要宣传一下一个并非 \(AAAT\) 的能保证时间复杂度的魔改 \(LCT\) 做法 (邪教)
更详细的介绍与时间复杂度分析:cnblog 洛谷blog
考虑用 \(splay\) 维护虚儿子,
但事实上如果单纯地让每个节点对应一个虚拟节点在对应的维护虚儿子的 \(splay\) 中,
像 \(AAAT\) 一样地写,并不能很好地保证单次 \(access\) 时间复杂度。
这主要是因为它删除节点时需将前驱(后继)找出来再删。
借鉴一下 \(Tarjan\) 的 \(Top\) \(Tree\) 论文,不难发现另一种方法
就是把虚儿子 \(splay\) 中的真正代表原先 \(LCT\) 中信息的点只让它们在叶子结点上
然后用一些辅助节点串起来,构成一个类似 \(leafytree\) 的结构
若在删一个节点时,由于其没有左右儿子,直接连带他的父节点从整个 \(splay\) 中移除,
再将其原先父节点的父节点旋到根,更新一下即可。
同时若是换节点的信息,同样直接改然后旋到根更新信息,十分方便。
而这样是可以保证单次 \(access\) 是均摊时间为一个 \(log\) 的,具体证明参见上面的链接。
回到这一题中,只需要维护路径覆盖的标记,但在下传标记时子树上除这条路径外的最小值是不会变的,
所以还要能知道每个点的 \(LCT\) 中的 \(splay\) 上全部子树的虚儿子的最小值的最小值。
那么在下传标记时这个点的子树最小值就是这个值与覆盖标记的最小值。
代码(被写Top Tree的大佬吊打)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int inf=0x7fffffff;
int n,m,x,y,v,opt,ans,root=1;
int son[N][2],anc[N],mn[N],sz[N],w[N],rt[N],rev[N],cov[N];
int cmn[N],rtmn[N];
int xx[N],yy[N];char ch;
void read(int &x){
x=0;ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
struct Splay{
int son[N<<2][2],anc[N<<2],mn[N<<2],wmn[N<<2],tot;queue<int> q;
void fix(int x){if(!x)return;mn[x]=min(min(mn[son[x][0]],mn[son[x][1]]),wmn[x]);}
bool p(int x){return son[anc[x]][1]==x;}
int newnode(){int tmp=0;if(!q.empty()){tmp=q.front();q.pop();}
else tmp=++tot;wmn[tmp]=mn[tmp]=inf;return tmp;}
void rotate(int x){int y=anc[x],xx=anc[y];bool b=p(x),bb=p(y);
anc[x]=xx;if(y)son[xx][bb]=x;son[y][b]=son[x][b^1];
anc[son[x][b^1]]=y;son[x][b^1]=y;anc[y]=x;fix(y);fix(x);fix(xx);
}
void splay(int x,int y){if(x==y)return;
for(int i=anc[x];i=anc[x],i!=y;rotate(x))
if(anc[i]!=y){if(p(x)==p(i))rotate(i);else rotate(x);}
}
void ins(int x,int y,int mn_){
wmn[x]=mn[x]=mn_;int tmp=rt[y],f=0;
if(!son[rt[y]][0])return anc[son[rt[y]][0]=x]=rt[y],fix(rt[y]);
while(1){
if(!tmp){
tmp=newnode();anc[son[anc[f]][p(f)]=tmp]=anc[f];
son[tmp][x>f]=x;anc[x]=tmp;
son[tmp][x<f]=f;anc[f]=tmp;
fix(f);fix(tmp);fix(anc[tmp]);
return splay(tmp,rt[y]);
}
f=tmp;tmp=son[tmp][x>tmp];
}
}
void clear(int x){wmn[x]=mn[x]=inf;son[x][0]=son[x][1]=anc[x]=0;
if(x>(n<<1))q.push(x);}
void del(int x,int y){
if(anc[x]==rt[y])return son[rt[y]][0]=0,fix(rt[y]),clear(x);
bool b=p(x);int yy=anc[x],xx=anc[yy];
if(xx==rt[y]){
anc[son[rt[y]][0]=son[yy][b^1]]=rt[y];
clear(x),clear(yy),fix(rt[y]);return;
}
anc[son[xx][p(yy)]=son[yy][b^1]]=xx;
clear(x);clear(yy);fix(xx);splay(xx,rt[y]);fix(rt[y]);
}
void display(int x,int xx,int mn_,int y){
wmn[xx]=mn[xx]=mn_;fix(xx);int yy=anc[x];anc[son[yy][p(x)]=xx]=yy;
clear(x);fix(yy);if(yy!=rt[y])splay(yy,rt[y]);fix(rt[y]);
}
}S;
bool p(int x){return son[anc[x]][1]==x;}
bool isroot(int x){return son[anc[x]][0]!=x&&son[anc[x]][1]!=x;}
void rev_(int x){rev[x]^=1;son[x][0]^=son[x][1]^=son[x][0]^=son[x][1];}
void fix(int x){if(!x)return;
cmn[x]=min(min(cmn[son[x][0]],cmn[son[x][1]]),w[x]);
rtmn[x]=min(min(rtmn[son[x][0]],rtmn[son[x][1]]),S.mn[rt[x]]);
mn[x]=min(cmn[x],rtmn[x]);
}
void cov_(int x,int v){if(!x)return;cmn[x]=w[x]=cov[x]=v;mn[x]=min(v,rtmn[x]);}
void pushdown(int x){
if(rev[x]){if(son[x][0])rev_(son[x][0]);
if(son[x][1])rev_(son[x][1]);rev[x]=0;}
if(cov[x]){if(son[x][0])cov_(son[x][0],cov[x]);
if(son[x][1])cov_(son[x][1],cov[x]);cov[x]=0;}
}
void pushall(int x){if(!isroot(x))pushall(anc[x]);pushdown(x);}
void rotate(int x){int y=anc[x],xx=anc[y];bool b=p(x),bb=p(y);
anc[x]=xx;if(!isroot(y))son[xx][bb]=x;son[y][b]=son[x][b^1];
anc[son[x][b^1]]=y;son[x][b^1]=y;anc[y]=x;fix(y);fix(x);fix(xx);
}
int findrt(int x){if(isroot(x))return x;return findrt(anc[x]);}
void splay(int x){pushall(x);int y=findrt(x);
if(anc[y]&&x!=y)S.display(y,x,mn[y],anc[y]);
for(int i=anc[x];i=anc[x],!isroot(x);rotate(x))
if(!isroot(i)){if(p(i)==p(x))rotate(i);else rotate(x);}
}
int access(int x){int y=0;
for(;x;y=x,x=anc[x]){
splay(x);
if(y&&son[x][1])S.display(y,son[x][1],mn[son[x][1]],x);
else if(!y&&son[x][1])S.ins(son[x][1],x,mn[son[x][1]]);
else if(y&&!son[x][1])S.del(y,x);
son[x][1]=y;fix(x);
}
return y;
}
void makeroot(int x){access(x);splay(x),rev_(x);}
void split(int x,int y){makeroot(x),access(y),splay(y);}
void link(int x,int y){split(x,y);anc[x]=y;S.ins(x,y,mn[x]);}
void path_cov(int x,int y,int v){split(x,y);splay(y);pushall(y);cov_(y,v);}
int main(){
read(n),read(m);S.tot=n<<1;
for(int i=1;i<n;i++)read(xx[i]),read(yy[i]);
cmn[0]=rtmn[0]=mn[0]=w[0]=S.mn[0]=S.wmn[0]=inf;
for(int i=1;i<=n;i++)read(w[i]),fix(i),rt[i]=i+n,S.mn[rt[i]]=S.wmn[rt[i]]=inf;
for(int i=1;i<n;i++)link(xx[i],yy[i]);
read(root);
while(m--){
read(opt);read(x);
if(opt==1)root=x;
if(opt==2)read(y),read(v),path_cov(x,y,v);
if(opt==3){
split(root,x);
ans=min(S.mn[rt[x]],w[x]);
printf("%d\n",ans);
}
}
}