[bzoj3510]首都

来源:互联网 发布:淘宝上买中药材可靠吗 编辑:程序博客网 时间:2024/05/16 23:51

题目大意

一开始有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);    }}
0 0
原创粉丝点击