首都
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;
}