【刷题】BZOJ 3510 首都
Description
在X星球上有N个国家,每个国家占据着X星球的一座城市。由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的。
X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失,而B国的国土也将归A国管辖。A国国王为了加强统治,会在A国和B国之间修建一条公路,即选择原A国的某个城市和B国某个城市,修建一条连接这两座城市的公路。
同样为了便于统治自己的国家,国家的首都会选在某个使得其他城市到它距离之和最小的城市,这里的距离是指需要经过公路的条数,如果有多个这样的城市,编号最小的将成为首都。
现在告诉你发生在X星球的战事,需要你处理一些关于国家首都的信息,具体地,有如下3种信息需要处理:
1、A x y:表示某两个国家发生战乱,战胜国选择了x城市和y城市,在它们之间修建公路(保证其中城市一个在战胜国另一个在战败国)。
2、Q x:询问当前编号为x的城市所在国家的首都。
3、Xor:询问当前所有国家首都编号的异或和。
Input
第一行是整数N,M,表示城市数和需要处理的信息数。
接下来每行是一个信息,格式如题目描述(A、Q、Xor中的某一种)。
Output
输出包含若干行,为处理Q和Xor信息的结果。
Sample Input
10 10
Xor
Q 1
A 10 1
A 1 4
Q 4
Q 10
A 7 6
Xor
Q 7
Xor
Sample Output
11
1
1
1
2
6
2
HINT
对于100%的数据,2<=N<=100000,1<=M<=200000。
Solution
这个必须膜拜,orz CJ FlashHu 大佬,大佬的讲解看这里
大佬把算法优化到了 \(O(nlogn)\) ,太强了
首先有动态连边,那么LCT
然后看首都在一棵树的什么位置?明显的重心
对于首都,直接用并查集维护(可不可以用维护边双的方法?)
对于维护两颗树合并后的重心,我们最开始可以知道合并后的重心一定是两个原重心之间的路径上的一个点,然后二分这个位置在哪里
两个点link后,把两个原重心拉链,那么路径就出来了
考虑在路径构成的splay上二分,抽象地把路径看成一个数轴,那么要找重心,就是找那个点左边的权重和右边的权重均小于等于总点数的一半
check条件就是这个,根据这个二分就好了,注意如果总点数是偶数,那么要找编号最小的
然后因为要维护子树权重(就是大小),那么要用LCT维护子树信息,会有虚子树的出现
具体二分过程可以看代码,当然也可以看大佬博客,他讲的很清楚
#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=100000+10,inf=0x3f3f3f3f;
int n,m,ans,f[MAXN];
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
struct LinkCut_Tree{
int fa[MAXN],rev[MAXN],ch[MAXN][2],sum[MAXN],Isum[MAXN],cnt,stack[MAXN];
inline bool nroot(int x)
{
return lc(fa[x])==x||rc(fa[x])==x;
}
inline void reverse(int x)
{
std::swap(lc(x),rc(x));
rev[x]^=1;
}
inline void pushup(int x)
{
sum[x]=sum[lc(x)]+sum[rc(x)]+Isum[x]+1;
}
inline void pushdown(int x)
{
if(rev[x])
{
if(lc(x))reverse(lc(x));
if(rc(x))reverse(rc(x));
rev[x]=0;
}
}
inline void rotate(int x)
{
int f=fa[x],p=fa[f],c=(rc(f)==x);
if(nroot(f))ch[p][rc(p)==f]=x;
fa[ch[f][c]=ch[x][c^1]]=f;
fa[ch[x][c^1]=f]=x;
fa[x]=p;
pushup(f);
pushup(x);
}
inline void splay(int x)
{
cnt=0;
stack[++cnt]=x;
for(register int i=x;nroot(i);i=fa[i])stack[++cnt]=fa[i];
while(cnt)pushdown(stack[cnt--]);
for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
pushup(x);
}
inline void access(int x)
{
for(register int y=0;x;x=fa[y=x])
{
splay(x);
Isum[x]+=sum[rc(x)];
rc(x)=y;
Isum[x]-=sum[rc(x)];
pushup(x);
}
}
inline void makeroot(int x)
{
access(x);splay(x);reverse(x);
}
inline void split(int x,int y)
{
makeroot(x);access(y);splay(y);
}
inline void link(int x,int y)
{
makeroot(x);access(y);splay(y);
fa[x]=y;
Isum[y]+=sum[x];
pushup(y);
}
inline void modify(int u,int v)
{
int x=v,all=sum[x],even=all&1,nw=inf,lsum=0,rsum=0,nl,nr;
while(x)
{
pushdown(x);
nl=sum[lc(x)]+lsum,nr=sum[rc(x)]+rsum;
if(nl<=(all>>1)&&nr<=(all>>1))
{
if(even){nw=x;break;}
else chkmin(nw,x);
}
if(nl<nr)lsum+=sum[lc(x)]+Isum[x]+1,x=rc(x);
else rsum+=sum[rc(x)]+Isum[x]+1,x=lc(x);
}
splay(nw);
ans=ans^u^v^nw;
f[u]=f[v]=f[nw]=nw;
}
};
LinkCut_Tree T;
#undef lc
#undef rc
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline int found(int x)
{
if(f[x]!=x)f[x]=found(f[x]);
return f[x];
}
int main()
{
read(n);read(m);
for(register int i=1;i<=n;++i)f[i]=i,T.sum[i]=1,ans^=i;
while(m--)
{
char opt[5];scanf("%s",opt);
if(opt[0]=='A')
{
int x,y;read(x);read(y);
T.link(x,y);
x=found(x),y=found(y);
T.split(x,y);T.modify(x,y);
}
if(opt[0]=='Q')
{
int x;read(x);
write(found(x),'\n');
}
if(opt[0]=='X')write(ans,'\n');
}
return 0;
}