LOJ#6038. 「雅礼集训 2017 Day5」远行 (LCT维护两点距离+并查集维护树的直径)

题面:https://loj.ac/problem/6038

 

 

题解

居然在原题重测(原题重错、新题爆错)的考场上切了一道LCT的题

一看就是维护森林中每棵树的直径

以为可以用倍增LCA来求两点距离,然而它强制在线

想了一下,发现可以用LCT来求两点距离

因为所有的边权都为1,所以我们把一条链spilt出来后得到的链的节点数-1就是两点的距离

然后合并两棵树的时候用并查集来记录一下直径的长度以及两个端点,用LCT算4个点对的距离然后更新即可

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 300005
namespace LCT{
	int fa[N],ch[N][2],siz[N];
	bool rev[N];
	bool nrt(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
	bool pdc(int x){return ch[fa[x]][1]==x;}
	void pushdown(int x){
		if(rev[x]){
			swap(ch[x][0],ch[x][1]);
			if(ch[x][0])rev[ch[x][0]]^=1;
			if(ch[x][1])rev[ch[x][1]]^=1;
			rev[x]=0;
		}
	}
	void pushup(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}
	void rot(int x){
		int y=fa[x],z=fa[y];
		bool flg=pdc(x);
		if(nrt(y))ch[z][pdc(y)]=x;
		if(ch[y][flg]=ch[x][flg^1])
			fa[ch[y][flg]]=y;
		ch[x][flg^1]=y;
		fa[y]=x;fa[x]=z;
		pushup(y);pushup(x);
	}
	void pdpath(int x){if(nrt(x))pdpath(fa[x]);pushdown(x);}
	void splay(int x){
		pdpath(x);
		for(;nrt(x);rot(x))
			if(nrt(fa[x]))rot(pdc(x)==pdc(fa[x])?fa[x]:x);
	}
	void Access(int x){
		for(int i=0;x;i=x,x=fa[x]){
			splay(x);
			ch[x][1]=i;
			pushup(x);
		}
	}
	void beroot(int x){
		Access(x);
		splay(x);
		rev[x]^=1;
	}
	void link(int x,int y){
		beroot(x);
		fa[x]=y;
	}
	int dis(int x,int y){
		beroot(x);Access(y);splay(y);
		return siz[y];
	}
}//----LCT----
int fa[N],A[N],B[N],H[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int hh,tmp1,tmp2;
void update(int cd,int t1,int t2)
{
	if(hh<cd){hh=cd;tmp1=t1;tmp2=t2;}
}
void HB(int x,int y)// !!!!
{
	x=find(x);y=find(y);
	if(x!=y){
		fa[y]=x;
		hh=H[x];tmp1=A[x];tmp2=B[x];
		update(H[y],A[y],B[y]);
		update(LCT::dis(A[x],A[y]),A[x],A[y]);
		update(LCT::dis(A[x],B[y]),A[x],B[y]);
		update(LCT::dis(B[x],A[y]),B[x],A[y]);
		update(LCT::dis(B[x],B[y]),B[x],B[y]);
		H[x]=hh;A[x]=tmp1;B[x]=tmp2;
	}
}
int getans(int x)
{
	int f=find(x);
	return max(LCT::dis(x,A[f]),LCT::dis(x,B[f]));
}
int main()
{
	int tp,op,n,Q,i,u,v,ans=0;
	tp=gi();n=gi();Q=gi();
	for(i=1;i<=n;i++)fa[i]=A[i]=B[i]=i,H[i]=1;
	while(Q--){
		op=gi();
		if(op==1){
			u=gi();v=gi();
			if(tp)u^=ans,v^=ans;
			LCT::link(u,v);
			HB(u,v);
		}
		else{
			u=gi();
			if(tp)u^=ans;
			ans=getans(u)-1;// !!!!
			printf("%d\n",ans);
		}
	}
}

hehe

考试的时候不知道为什么RE了,后来发现自己没有记录lastans

再交一发还是RE,换了一种合并的方式就A了

(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・(・∀・*)

Σ(っ °Д °;)っ≧ ﹏ ≦Σ(っ °Д °;)っ≧ ﹏ ≦Σ(っ °Д °;)っ≧ ﹏ ≦Σ(っ °Д °;)っ≧ ﹏ ≦Σ(っ °Д °;)っ≧ ﹏ ≦Σ(っ °Д °;)っ

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值