luogu P4271 [USACO18FEB]New Barns P
题面传送门
LCT常数其实并没有我们想象的那么大。
这道题有连边操作肯定想到LCT。
我们可以用LCT动态维护树的直径。
具体的,如果把两棵树合并,那么直径一定是这两棵树的四个端点的两个。这样的话分\(6\)种情况讨论即可。
但是我们可以发现这道题完全不用这样讨论,因为有一棵树是一个点,所以直接砍掉一半。
LCT开了O2实现好一点直接吊打树剖。
code:
#include<cstdio>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,un,wn,fa[100039],l[100039],r[100039],cnt,ans1,ans2,ans3;
char op;
I int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct linkcuttree{
int fa[100039],l[100039],r[100039],siz[100039],flag[100039],st[100039],sh;
I void up(int x){siz[x]=siz[l[x]]+siz[r[x]]+1;}
I void swap(int &x,int &y){x^=y^=x^=y;}
I void push(int x){x&&(swap(l[x],r[x]),flag[x]^=1);}
I void pushdown(int x){flag[x]&&(push(l[x]),push(r[x]),flag[x]=0);}
I int child(int x){return l[fa[x]]==x||r[fa[x]]==x;}
I int wrt(int x) {return l[fa[x]]==x;}
I void rotate(int x){
int y=fa[x],z=fa[y],b=(x==l[y]?r[x]:l[x]);child(y)&&((y==l[z]?l[z]:r[z])=x);
(x==l[y])?(r[x]=y,l[y]=b):(l[x]=y,r[y]=b);fa[x]=z;fa[y]=x;b&&(fa[b]=y);up(y);up(x);
}
I void splay(int x){
int y=x;st[sh=1]=x;while(child(y)) st[++sh]=y=fa[y];
while(sh) pushdown(st[sh--]);while(child(x))child(fa[x])&&(rotate(wrt(x)^wrt(fa[x])?x:fa[x]),0),rotate(x);
}
I void access(int x){for(int y=0;x;x=fa[y=x]) splay(x),r[x]=y,up(x);}
I void makeroot(int x){access(x);splay(x);push(x);}
I void link(int x,int y){makeroot(x);fa[x]=y;}
I void split(int x,int y){makeroot(x);access(y);splay(y);}
I void cut(int x,int y){split(x,y);r[y]=fa[x]=0;}
}s;
I int check(int x,int y){s.split(x,y);return s.siz[y];}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d",&n);for(i=1;i<=n+1;i++) fa[i]=i,l[i]=r[i]=i;
while(n--){
op=getchar();while(op<'A'||op>'Z') op=getchar();scanf("%d",&x);
if(op=='Q')un=find(x),ans1=check(l[un],x),ans2=check(r[un],x),printf("%d\n",max(ans1,ans2)-1);
else{
cnt++;if(x==-1) continue;un=find(x);s.link(x,cnt);
fa[cnt]=un;ans1=check(l[un],cnt),ans2=check(r[un],cnt);ans3=check(l[un],r[un]);
(max(ans1,ans2)>ans3)&&((ans1>ans2?r[un]:l[un])=cnt);
}
}
}