把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷4299】首都(LCT维护树的重心)

点此看题面

  • 初始有\(n\)个点,\(q\)次操作,分为三种:在未连通的两点\(x,y\)间连一条边;询问\(x\)所在树的重心;询问所有树的重心的异或和。
  • \(n\le10^5,q\le2\times10^5\)

\(LCT\)维护树的重心

首先必须知道一个重要结论:两棵树合并之后,新的重心必然在原先两个重心间的路径上

我们用\(LCT\)维护合并操作,那么想要找到新的重心,就是利用\(LCT\)把这条链抠出来,然后在\(Splay\)上二分。

此次又利用到重心的另一个性质,这个就比较常见了:重心的各子树大小都小于等于整棵树的一半。

\(LCT\)维护子树信息应该也算个经典问题了,对每个点维护个虚儿子总大小即可。

而要询问所有树的重心的异或和,因为一次操作只会减少两个重心,新增一个重心,直接开个变量记录异或和就好了。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,f[N+5],g[N+5];I int F(CI x) {return f[x]^x?f[x]=F(f[x]):x;}//并查集指向重心
class LinkCutTree
{
	private:
		#define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+O[x].ISz+1)
		#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
		#define Wh(x) (O[O[x].F].S[1]==x)
		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
		#define PD(x) (O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0))
		#define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)
		#define MR(x) (Ac(x),S(x),Re(x))
		struct node {int Sz,ISz,R,F,S[2];}O[N+5];
		I void Ro(RI x) {RI f=O[x].F,p=O[f].F,d=Wh(x);
			!IR(f)&&(O[p].S[Wh(f)]=x),O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f);}
		int St[N+5];I void S(RI x) {RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;
			W(T) PD(St[T]),--T;W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(f)^Wh(x)?x:f),0),Ro(x);PU(x);}
		I void Ac(RI x) {for(RI y=0;x;x=O[y=x].F) S(x),O[x].ISz+=O[O[x].S[1]].Sz-O[y].Sz,O[x].S[1]=y,PU(x);}
	public:
		I void Init() {for(RI i=1;i<=n;++i) O[i].Sz=1;}
		I void Link(CI x,CI y) {MR(x),Ac(y),S(y),O[O[x].F=y].ISz+=O[x].Sz,PU(y);}
		I int Get(CI x,CI y,CI s)//在x,y路径上二分新的重心
		{
			MR(x),Ac(y),S(y);RI k=y,p=n,ls,rs,L=0,R=0;W(k)
			{
				PD(k),ls=O[O[k].S[0]].Sz+L,rs=O[O[k].S[1]].Sz+R,ls<=(s>>1)&&rs<=(s>>1)&&(p=min(p,k));//判断当前点是否为重心
				ls>rs?(R+=O[k].Sz,k=O[k].S[0],R-=O[k].Sz):(L+=O[k].Sz,k=O[k].S[1],L-=O[k].Sz);//进入较大的子树
			}return S(p),p;
		}
}LCT;
int main()
{
	RI Qt,i,t=0;for(scanf("%d%d",&n,&Qt),LCT.Init(),i=1;i<=n;++i) t^=(f[i]=i),g[i]=1;//初始化
	RI x,y,fx,fy,fz;char op[5];W(Qt--) switch(scanf("%s",op),op[0])
	{
		case 'A':scanf("%d%d",&x,&y),t^=(fx=F(x))^(fy=F(y)),LCT.Link(x,y);//消去原本重心的贡献;连边
			fz=LCT.Get(fx,fy,g[fx]+g[fy]),t^=(f[fx]=f[fy]=f[fz]=fz),g[fz]=g[fx]+g[fy];break;//求出新的重心,修改并查集
		case 'Q':scanf("%d",&x),printf("%d\n",F(x));break;case 'X':printf("%d\n",t);break;//询问
	}return 0;
}
posted @ 2021-07-14 19:39  TheLostWeak  阅读(102)  评论(0编辑  收藏  举报