Luogu P4271 [USACO18FEB]New Barns P
题意
给一个一开始没有点的图,有 \(q\) 次操作,每次为加点连边或者查询一个点到连通块内所有点的距离最大值。
\(\texttt{Data Range}:1\leq q\leq 10^5\)
题解
跟「雅礼集训 2017 Day5」远行很像的一个题,都是 LCT 维护直径。
注意到树上一个点到其他点的距离最大值只可能在直径的两个端点处取到,而且又存在加边操作,所以可以直接使用 LCT 来维护。
当合并两个连通块的时候需要在两个连通块各自的直径端点中选两个成为新的直径,需要讨论 \(6\) 种情况,这个暴力搞就行了。
但是这个题不用这么麻烦。因为每一次合并的一边是一个点,所以只需要讨论两次就好了。
同时,维护直径的两个端点和直径的距离可以使用并查集来维护,查询两点距离的话就先 split
把两个点的路径拉出来放到同一个 Splay 上,再用根节点的大小减 \(1\) 即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=3e5+51;
ll n,c,d,x,fx,fy,mx,lx,rx;
char op;
ll ffa[MAXN],l[MAXN],r[MAXN],dist[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline ll find(ll x)
{
return x==ffa[x]?x:ffa[x]=find(ffa[x]);
}
namespace LCT{
struct Node{
ll fa,rv,sz;
ll ch[2];
};
struct LinkCutTree{
Node nd[MAXN];
ll st[MAXN];
#define ls nd[x].ch[0]
#define rs nd[x].ch[1]
inline bool nroot(ll x)
{
return nd[nd[x].fa].ch[0]==x||nd[nd[x].fa].ch[1]==x;
}
inline void update(ll x)
{
nd[x].sz=nd[ls].sz+nd[rs].sz+1;
}
inline void reverse(ll x)
{
swap(ls,rs),nd[x].rv^=1;
}
inline void spread(ll x)
{
if(nd[x].rv)
{
ls?reverse(ls):(void)1,rs?reverse(rs):(void)1;
nd[x].rv=0;
}
}
inline void rotate(ll x)
{
ll fa=nd[x].fa,gfa=nd[fa].fa;
ll dir=nd[fa].ch[1]==x,son=nd[x].ch[!dir];
if(nroot(fa))
{
nd[gfa].ch[nd[gfa].ch[1]==fa]=x;
}
nd[x].ch[!dir]=fa,nd[fa].ch[dir]=son;
if(son)
{
nd[son].fa=fa;
}
nd[fa].fa=x,nd[x].fa=gfa,update(fa);
}
inline void splay(ll x)
{
ll fa=x,gfa,cur=0;
st[++cur]=fa;
while(nroot(fa))
{
st[++cur]=fa=nd[fa].fa;
}
while(cur)
{
spread(st[cur--]);
}
while(nroot(x))
{
fa=nd[x].fa,gfa=nd[fa].fa;
if(nroot(fa))
{
rotate((nd[fa].ch[0]==x)^(nd[gfa].ch[0]==fa)?x:fa);
}
rotate(x);
}
update(x);
}
inline void access(ll x)
{
for(register int i=0;x;x=nd[i=x].fa)
{
splay(x),rs=i,update(x);
}
}
inline void makeRoot(ll x)
{
access(x),splay(x),reverse(x);
}
inline ll findRoot(ll x)
{
access(x),splay(x);
while(ls)
{
spread(x),x=ls;
}
return x;
}
inline void split(ll x,ll y)
{
makeRoot(x),access(y),splay(y);
}
inline void link(ll x,ll y)
{
makeRoot(x);
if(findRoot(y)!=x)
{
nd[x].fa=y;
}
}
#undef ls
#undef rs
};
}
LCT::LinkCutTree lct;
inline ll getDist(ll x,ll y)
{
lct.split(x,y);
return lct.nd[y].sz-1;
}
int main()
{
n=read();
for(register int i=1;i<=n;i++)
{
ffa[i]=l[i]=r[i]=i,lct.nd[i].sz=1;
}
for(register int i=0;i<n;i++)
{
cin>>op,x=read();
if(op=='B')
{
++c;
if(x==-1)
{
continue;
}
fx=c,fy=find(x),mx=dist[fx],lx=l[fx],rx=r[fx];
if(mx<dist[fy])
{
lx=l[fy],rx=r[fy],mx=dist[fy];
}
lct.link(c,x);
if((d=getDist(l[fx],l[fy]))>mx)
{
mx=d,lx=l[fx],rx=l[fy];
}
if((d=getDist(l[fx],r[fy]))>mx)
{
mx=d,lx=l[fx],rx=r[fy];
}
l[fx]=lx,r[fx]=rx,dist[fx]=mx,ffa[fy]=fx;
}
if(op=='Q')
{
fx=find(x);
printf("%d\n",max(getDist(x,l[fx]),getDist(x,r[fx])));
}
}
}