首都

XIX.首都

一句话题意:维护一棵森林,支持查询某棵树的重心以及所有树的重心的异或和。

众所周知,重心有如下性质:将两棵树之间连一条边后,新树的重心在原两棵树重心的连线上。

根据这一性质,我想了半天也没有想出来什么美妙的算法主要还是我splay没学好

首先,这道题正解有两个,一是LCT+启发式合并(最暴力的断边重连那种),复杂度\(O(m\log^2 n)\);二是LCT+平衡树上二分,复杂度\(O(m\log n)\)

第一种方法过于暴力,没有任何技术含量,但我仍然写不出来,这里就不具体介绍了。

然后第二种方法,就是维护子树大小。每当我们连一条边后,将原两棵树重心连线split出来。然后在splay上二分,保证复杂度是一个\(\log\)

局部代码:

\(cen\)\(centroid\),重心之义;find(x)即为找到\(x\)的重心)

inline void link(int u,int v){
	makeroot(u),makeroot(v),t[u].fa=v,t[v].si+=t[u].sr,pushup(v);
	u=find(u),v=find(v),split(u,v);
	int ls=0,rs=0,mn=0x3f3f3f3f,x=v,lim=t[x].sr>>1;//ls&rs:the size of the subtree outside lson and rson
	while(x){
		pushdown(x);
		int lsum=ls+t[lson].sr,rsum=rs+t[rson].sr;//the size of x's to sons
		if(lsum<=lim&&rsum<=lim)mn=min(mn,x);
		if(lsum<rsum)ls+=t[x].sr-t[rson].sr,x=rson;//add the size of the subtree of x minus the size of the subtree of rson
		else rs+=t[x].sr-t[lson].sr,x=lson;
	}
	cen[u]=cen[v]=cen[mn]=mn,res^=u^v^mn;
}

总代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,cen[100100],res;
int find(int x){return cen[x]==x?x:cen[x]=find(cen[x]);}
#define lson t[x].ch[0]
#define rson t[x].ch[1]
struct LCT{
	int fa,ch[2],si,sr;
	bool rev;
}t[100100];
inline int identify(int x){
	if(x==t[t[x].fa].ch[0])return 0;
	if(x==t[t[x].fa].ch[1])return 1;
	return -1;
}
inline void pushup(int x){
	t[x].sr=t[x].si+1;
	if(lson)t[x].sr+=t[lson].sr;
	if(rson)t[x].sr+=t[rson].sr;
}
inline void REV(int x){t[x].rev^=1,swap(lson,rson);}
inline void pushdown(int x){
	if(!t[x].rev)return;
	if(lson)REV(lson);
	if(rson)REV(rson);
	t[x].rev=0;
}
inline void rotate(int x){
	register int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
	if(b)t[b].fa=y;t[y].ch[dirx]=b;
	t[y].fa=x,t[x].ch[!dirx]=y;
	pushup(y),pushup(x);
}
inline void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
inline void splay(int x){for(pushall(x);identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(x)==identify(t[x].fa)?t[x].fa:x);}
inline void access(int x){for(register int y=0;x;x=t[y=x].fa)splay(x),t[x].si+=t[rson].sr-t[y].sr,rson=y,pushup(x);}
inline void makeroot(int x){access(x),splay(x),REV(x);}
inline int findroot(int x){access(x),splay(x),pushdown(x);while(lson)x=lson,pushdown(x);splay(x);return x;}
inline void split(int x,int y){makeroot(x),access(y),splay(y);}
inline void link(int u,int v){
	makeroot(u),makeroot(v),t[u].fa=v,t[v].si+=t[u].sr,pushup(v);
	u=find(u),v=find(v),split(u,v);
	int ls=0,rs=0,mn=0x3f3f3f3f,x=v,lim=t[x].sr>>1;
	while(x){
		pushdown(x);
		int lsum=ls+t[lson].sr,rsum=rs+t[rson].sr;
		if(lsum<=lim&&rsum<=lim)mn=min(mn,x);
		if(lsum<rsum)ls+=t[x].sr-t[rson].sr,x=rson;
		else rs+=t[x].sr-t[lson].sr,x=lson;
	}
	cen[u]=cen[v]=cen[mn]=mn,res^=u^v^mn;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)cen[i]=i,res^=i;
	for(int i=1,x,y;i<=m;i++){
		char s[5];
		scanf("%s",s);
		if(s[0]=='A')scanf("%d%d",&x,&y),link(x,y);
		if(s[0]=='Q')scanf("%d",&x),printf("%d\n",find(x));
		if(s[0]=='X')printf("%d\n",res);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14602125.html