题解:P14486 [集训队互测 2018] 北校门外的未来
重构树
既然题目里新加入的点编号是单调递增的,那我们不妨换个思路,尝试以“编号最大的点”为核心来重构这棵树
在这棵新鲜出炉的重构树
转化为贪心
既然明确了只能往祖先跳,那查询两个点
这里的贪心策略很简单:首先贪心地往深度最浅的合法节点跳,直到再跳一步就会越过 LCA 为止。假设在这个临界状态下,两边合计跳了
- 如果此时两边的端点都能直接一步跳到 LCA,或者它们彼此之间满足直接跳跃的条件,那皆大欢喜,总步数就是
c+2 。 - 如果差了一点点没法直接到位,那它们必须各自再向上走一步才能汇合。由于此时它们已经紧紧贴着 LCA 了,再跳一步必然能抱在一起,总步数就是
c+3 。
为什么必须用 LCT 维护?
如果这道题是静态的,那我们直接建出
没办法,遇到这种动态树问题,只能请出神秘的 LCT 来动态维护这棵重构树了。在 LCT 的辅助图上,我们可以把边分为代表直接连通的“实边”,以及代表当前辅助图同父节点的“虚边”。其实 LCT 中的 Access 操作,在这里的本质就是在这棵动态树上提取出这种“贪心跳跃”的极长连通块(也就是实边链)。查询的时候,只要从 gfir 函数),就能顺利得到最终的最短步数啦!
代码实现
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,tt,tt2,cn,op[N*5],rx[N*5],ry[N*5];
int ls[N],to[N<<1],nx[N<<1],h[N],to2[N],nx2[N];
int pos[N],fa[N],dwn[N],low[N],sz[N],dep[N];
int son[N],tp[N],rfn[N],ed[N],sk[N],skp;
int dsu[N];
void add(int x,int y){to[++tt]=y;nx[tt]=ls[x];ls[x]=tt;}
void add2(int x,int y){to2[++tt2]=y;nx2[tt2]=h[x];h[x]=tt2;}
int F(int x){return dsu[x]==x?x:dsu[x]=F(dsu[x]);}
void dfs1(int x){
while(skp&&pos[sk[skp]]>pos[x])dwn[sk[skp--]]=x;
sk[++skp]=x;sz[x]=1;
for(int i=h[x];i;i=nx2[i]){
int y=to2[i];
fa[y]=x;dep[y]=dep[x]+1;
dfs1(y);sz[x]+=sz[y];
if(sz[y]>sz[son[x]])son[x]=y;
}
}
void dfs2(int x){
rfn[x]=++cn;
if(son[x]){tp[son[x]]=tp[x];dfs2(son[x]);}
for(int i=h[x];i;i=nx2[i]){
int y=to2[i];
if(y==son[x])continue;
tp[y]=y;dfs2(y);
}
ed[x]=cn;
}
int lca(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
x=fa[tp[x]];
}
return dep[x]<dep[y]?x:y;
}
int gtp(int x,int y){
while(tp[x]!=tp[y]){
if(fa[tp[x]]==y)return tp[x];
x=fa[tp[x]];
}
return son[y];
}
bool gfir(int x,int y){
int z=gtp(x,y);
return rfn[x]<=rfn[low[z]]&&rfn[low[z]]<=ed[x];
}
struct LCT{
int t[N][2],f[N],cf[N],w[N],c[N],sn[N];
bool nr(int x){return f[x]&&(t[f[x]][0]==x||t[f[x]][1]==x);}
bool dir(int x){return t[f[x]][1]==x;}
void up(int x){w[x]=w[t[x][0]]+w[t[x][1]]+c[x];}
void rot(int x){
int y=f[x],z=f[y],xs=dir(x),ys=dir(y),W=t[x][xs^1];
if(nr(y))t[z][ys]=x;
t[y][xs]=W;t[x][xs^1]=y;
if(W)f[W]=y;
f[y]=x;f[x]=z;
up(y);up(x);
}
void splay(int x){
while(nr(x)){
int y=f[x];
if(!nr(y))rot(x);
else if(dir(x)==dir(y))rot(y),rot(x);
else rot(x),rot(x);
}
}
void acc(int x){
for(int y=0;x;y=x,x=f[x])splay(x),t[x][1]=y,up(x);
}
int mktp(int x){
splay(x);
if(!t[x][0])return f[x];
x=t[x][0];
while(t[x][1])x=t[x][1];
splay(x);t[x][1]=0;up(x);
return x;
}
void mdf(int x,int W){splay(x);c[x]=W;up(x);}
int getT(int x){splay(x);while(t[x][0])x=t[x][0];return x;}
int getB(int x){splay(x);while(t[x][1])x=t[x][1];return x;}
void ins(int x,int y){
if(x>y){f[y]=cf[y]=x;c[y]=w[y]=1;return;}
int u,v=dwn[y];u=mktp(v);
mdf(y,c[v]);mdf(v,!u);
f[y]=u;f[v]=y;
if(sn[u]==v)sn[u]=y;
if(u)sn[y]=v;
int z=0,nw=0,lw=x;
while(x){
splay(x);t[x][1]=z;up(x);
if(w[x]){
while(x){
if(w[t[x][1]])x=t[x][1];
else if(c[x])break;
else x=t[x][0];
}
if(x>y)break;
u=mktp(x);
if(u>y)break;
splay(lw);t[lw][1]=0;
if(sn[lw]){
splay(sn[lw]);f[sn[lw]]=u;mdf(sn[lw],1);sn[lw]=0;
}
int pre=cf[x];mdf(x,0);
x=getB(x);splay(x);
if(nw)f[nw]=x,sn[x]=t[x][1]=getT(nw);
nw=x;z=0;lw=x=pre;
}else z=x,x=f[x];
}
cf[y]=cf[dwn[y]];cf[dwn[y]]=y;
if(nw)nw=getT(nw),mdf(nw,1),f[nw]=y;
}
pair<int,int> ask(int x,int y){
if(x==y)return {0,0};
acc(x);splay(x);
int dis=0,top=0,pre=x;
while(x){
if(x<y)top=x,x=t[x][0];
else x=t[x][1];
}
splay(top);x=t[top][1];
if(!w[x])return {0,pre};
dis=w[x];
while(x){
if(w[t[x][0]])x=t[x][0];
else if(c[x]){
if(t[x][0]){
x=t[x][0];
while(t[x][1])x=t[x][1];
return {dis,x};
}
return {dis,top};
}else top=x,x=t[x][1];
}
return {dis,top};
}
int qry(int x,int y){
if(x==y)return 0;
if(x>y)swap(x,y);
int la=lca(x,y);
if(la==y){
pair<int,int> A=ask(x,y);
int ans=A.first+2;
if(gfir(A.second,y))ans--;
return ans;
}
pair<int,int> A=ask(x,la),B=ask(y,la);
int ans=A.first+B.first+3;
if(gfir(A.second,la)&&gfir(B.second,la))ans--;
return ans;
}
}T;
int main(){
scanf("%d%d",&n,&m);
n=1;
for(int i=1;i<=m;++i){
scanf("%d%d%d",&op[i],&rx[i],&ry[i]);
n=max(n,ry[i]);
if(op[i]==1){
add(rx[i],ry[i]);
add(ry[i],rx[i]);
pos[ry[i]]=i;
}
}
for(int i=1;i<=n;++i)dsu[i]=i;
for(int x=1;x<=n;++x){
for(int i=ls[x];i;i=nx[i]){
int y=to[i];
if(F(y)<x){
add2(x,F(y));
dsu[F(y)]=x;
}
}
}
fa[n]=0;dep[n]=1;
dfs1(n);tp[n]=n;dfs2(n);
for(int x=1;x<=n;++x){
for(int i=ls[x];i;i=nx[i]){
int y=to[i];
if(x>y)low[gtp(y,x)]=y;
}
}
for(int i=1;i<=m;++i){
if(op[i]==1)T.ins(rx[i],ry[i]);
else printf("%d\n",T.qry(rx[i],ry[i]));
}
return 0;
}