您的位置:首页 > 其它

[bzoj3510]首都

2016-05-21 15:54 417 查看

题目大意

一开始有n个结点,没有边。

有三种操作:将两个结点间连一条边,并且保证两个结点属不同联通块。询问一个联通块中所有点到其距离和最小的点。询问所有联通块中所有点到其距离和最小的点的异或和。

启发式合并

显然是一片森林,要求维护重心。

可以用启发式合并的思路,把小的合到大的里面,然后调整原本大的树里的重心,显然这个重心只会朝着小树方向调整,而且最多移动小树大小步。

然后需要用LCT来维护森林,并且要动态维护子树大小,还要兹瓷换根。怎么维护呢?

设siz[x]表示x的所有轻儿子子树大小和,那么想要获得x子树大小可以access(x)然后结果就是siz[x]+1。

spaly中维护size表示子树大小以及sum表示子树siz的和。

那么只需要在虚实交换的时候改变siz就行了。

也就是y是x一个儿子,y对x的影响是sum[y]+size[y]的(注意得保证y是其所在splay的根节点)

代码如下:

void real_empty(int x){
splay(x,0);
if (!tree[x][1]) return;
int y=suc(x);
splay(y,x);
siz[x]+=sum[y]+size[y];
pp[y]=x;
tree[x][1]=0;
father[y]=0;
update(x);
}
void empty_real(int x,int y){
splay(x,0);
splay(y,0);
tree[x][1]=y;
father[y]=x;
pp[y]=0;
siz[x]-=sum[y]+size[y];
update(x);
}


那么因为siz不受实边影响,所以可以兹瓷换根操作。

实现方面:

1、每棵树用并查集维护,保存大小与重心。

2、注意考虑一颗树两个重心的情况!

3、将一条虚边变为实边必须保证y是x的儿子,所以access不能像以前一样大,每次要把x调整成splay中深度最小点,用kth操作解决。

4、因为换根所需要的翻转标记,所以求前驱、后继以及深度最小点均不能直接上(除非你顺便下传标记),最好都用kth操作解决,kth里注意下传标记。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int fa[maxn],big[maxn],zx[maxn];
int father[maxn],siz[maxn],size[maxn],sum[maxn],pp[maxn],tree[maxn][2],sta[maxn];
int tiaoshi[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,ans,top;
char ch;
int pd(int x){
return tree[father[x]][1]==x;
}
void update(int x){
size[x]=size[tree[x][0]]+size[tree[x][1]]+1;
sum[x]=sum[tree[x][0]]+sum[tree[x][1]]+siz[x];
}
void rotate(int x){
int y=father[x],z=pd(x);
father[x]=father[y];
if (father[y]) tree[father[y]][pd(y)]=x;
tree[y][z]=tree[x][1-z];
if (tree[x][1-z]) father[tree[x][1-z]]=y;
tree[x][1-z]=y;
father[y]=x;
update(y);
update(x);
if (pp[y]) pp[x]=pp[y],pp[y]=0;
}
void mark(int x){
if (!x) return;
bz[x]^=1;
swap(tree[x][0],tree[x][1]);
}
void down(int x){
if (bz[x]){
mark(tree[x][0]);
mark(tree[x][1]);
bz[x]=0;
}
}
void remove(int x,int y){
top=0;
while (x!=y){
sta[++top]=x;
x=father[x];
}
while (top){
down(sta[top]);
top--;
}
}
void splay(int x,int y){
remove(x,y);
while (father[x]!=y){
if (father[father[x]]!=y)
if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
rotate(x);
}
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int kth(int x,int y){
down(x);
if (size[tree[x][0]]+1==y) return x;
else if (size[tree[x][0]]>=y) return kth(tree[x][0],y);
else return kth(tree[x][1],y-size[tree[x][0]]-1);
}
int pre(int x){
splay(x,0);
return kth(tree[x][0],size[tree[x][0]]);
}
int suc(int x){
splay(x,0);
return kth(tree[x][1],1);
}
void real_empty(int x){ splay(x,0); if (!tree[x][1]) return; int y=suc(x); splay(y,x); siz[x]+=sum[y]+size[y]; pp[y]=x; tree[x][1]=0; father[y]=0; update(x); } void empty_real(int x,int y){ splay(x,0); splay(y,0); tree[x][1]=y; father[y]=x; pp[y]=0; siz[x]-=sum[y]+size[y]; update(x); }
void access(int x){
int y;
real_empty(x);
splay(x,0);
x=kth(x,1);
splay(x,0);
while (pp[x]){
y=pp[x];
real_empty(y);
empty_real(y,x);
splay(x,0);
x=kth(x,1);
splay(x,0);
}
}
void makeroot(int x){
access(x);
splay(x,0);
mark(x);
}
void merge(int x,int y){
int xx=getfa(x),yy=getfa(y);
ans^=zx[xx]^zx[yy];
if (big[xx]<big[yy]||big[xx]==big[yy]&&x>y){
swap(x,y);
swap(xx,yy);
}
makeroot(x);
makeroot(y);
splay(x,0);
splay(y,0);
siz[x]+=sum[y]+size[y];
update(x);
pp[y]=x;
big[xx]+=big[yy];
fa[yy]=xx;
zx[yy]=0;
int j=zx[xx],k;
while (j!=x){
access(j);
k=pre(j);
if (big[xx]%2==1&&big[xx]-siz[j]-1<=big[xx]/2||big[xx]%2==0&&big[xx]-siz[j]-1<big[xx]/2||big[xx]%2==0&&big[xx]-siz[j]-1==big[xx]/2&&j<k) break;
j=k;
}
zx[xx]=j;
ans^=j;
}
char get(){
char ch=getchar();
while (ch!='A'&&ch!='Q'&&ch!='X') ch=getchar();
return ch;
}
int main(){
freopen("cap.in","r",stdin);freopen("cap.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n) ans^=(zx[i]=i),big[i]=size[i]=1;
fo(i,1,m){
ch=get();
if (ch=='A'){
scanf("%d%d",&j,&k);
merge(j,k);
}
else if (ch=='Q'){
scanf("%d",&j);
j=getfa(j);
printf("%d\n",zx[j]);
}
else printf("%d\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: