【题解】Luogu SP8791 DYNALCA - Dynamic LCA

时间:2024-05-20 21:33:14

原题传送门

这题用Link-Cut-Tree解决,Link-Cut-Tree详解

这道题的难点就在如何求LCA:

我们珂以先对其中一个点进行access操作,然后对另一个点进行access操作,因为LCA到根的边一定都由第一次access变为实边了,在之后的这一次access操作的最后一条从虚边变为实边的边的父亲就是两点的LCA

回求LCA后,剩下的几乎是模板,但有两种做法

1.建立虚拟点n+1,保证一直是一颗有n+1个点的树,写起来比较无脑(lct部分珂以复制),常数比较大

#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline void Swap(register int &a,register int &b)
{
a^=b^=a^=b;
}
struct Link_Cut_Tree{
int c[N][2],fa[N],top,q[N],rev[N];
inline void pushdown(register int x)
{
if(rev[x])
{
register int l=c[x][0],r=c[x][1];
rev[l]^=1,rev[r]^=1,rev[x]^=1;
Swap(c[x][0],c[x][1]);
}
}
inline bool isroot(register int x)
{
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
inline void rotate(register int x)
{
int y=fa[x],z=fa[y],l,r;
l=c[y][0]==x?0:1;
r=l^1;
if(!isroot(y))
c[z][c[z][0]==y?0:1]=x;
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
}
inline void splay(register int x)
{
top=1;
q[top]=x;
for(register int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
for(register int i=top;i;--i)
pushdown(q[i]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
rotate(x);
}
}
inline void access(register int x)
{
for(register int t=0;x;t=x,x=fa[x])
{
splay(x);
c[x][1]=t;
}
}
inline void makeroot(register int x)
{
access(x);
splay(x);
rev[x]^=1;
}
inline void split(register int x,register int y)
{
makeroot(x);
access(y);
splay(y);
}
inline void cut(register int x,register int y)
{
split(x,y);
c[y][0]=0;
fa[x]=0;
}
inline void link(register int x,register int y)
{
makeroot(x);
fa[x]=y;
}
inline int LCA(register int x,register int y)
{
access(x);
int t;
for(t=0;y;t=y,y=fa[y])
{
splay(y);
c[y][1]=t;
}
return t;
}
}T;
int fa[N];
int main()
{
int n=read(),m=read();
for(register int i=1;i<=n;++i)
T.link(i,n+1),fa[i]=n+1;
while(m--)
{
char c=getchar();
while(c!='k'&&c!='t'&&c!='a')
c=getchar();
if(c=='k')
{
int x=read(),y=read();
T.cut(x,fa[x]);
fa[x]=y;
T.link(x,y);
}
else if(c=='t')
{
int x=read();
T.cut(x,fa[x]);
fa[x]=n+1;
T.link(x,n+1);
}
else
{
int x=read(),y=read();
T.makeroot(n+1);
write(T.LCA(x,y)),puts("");
}
}
return 0;
}

2.按照题目加边,删边(常数小,容易写错)

注意:cut和LCA操作是不能使用makeroot操作的,因为makeroot本身会修改树上的LCA关系,当然link操作使用makeroot是没有关系的

#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline void Swap(register int &a,register int &b)
{
a^=b^=a^=b;
}
struct Link_Cut_Tree{
int c[N][2],fa[N],top,q[N],rev[N];
inline void pushdown(register int x)
{
if(rev[x])
{
register int l=c[x][0],r=c[x][1];
rev[l]^=1,rev[r]^=1,rev[x]^=1;
Swap(c[x][0],c[x][1]);
}
}
inline bool isroot(register int x)
{
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
inline void rotate(register int x)
{
int y=fa[x],z=fa[y],l,r;
l=c[y][0]==x?0:1;
r=l^1;
if(!isroot(y))
c[z][c[z][0]==y?0:1]=x;
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
}
inline void splay(register int x)
{
top=1;
q[top]=x;
for(register int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
for(register int i=top;i;--i)
pushdown(q[i]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
rotate(x);
}
}
inline void access(register int x)
{
for(register int t=0;x;t=x,x=fa[x])
{
splay(x);
c[x][1]=t;
}
}
inline void makeroot(register int x)
{
access(x);
splay(x);
rev[x]^=1;
}
inline void cut(register int x)
{
access(x);
splay(x);
fa[c[x][0]]=0;
c[x][0]=0;
}
inline void link(register int x,register int y)
{
makeroot(x);
fa[x]=y;
}
inline int LCA(register int x,register int y)
{
access(x);
int t;
for(t=0;y;t=y,y=fa[y])
{
splay(y);
c[y][1]=t;
}
return t;
}
}T;
int fa[N];
int main()
{
int n=read(),m=read();
while(m--)
{
char c=getchar();
while(c!='k'&&c!='t'&&c!='a')
c=getchar();
if(c=='k')
{
int x=read(),y=read();
T.link(x,y);
}
else if(c=='t')
{
int x=read();
T.cut(x);
}
else
{
int x=read(),y=read();
write(T.LCA(x,y)),puts("");
}
}
return 0;
}