AmazingCounters.com

[BZOJ]3674: 可持久化并查集加强版

题目大意:n个点,三种操作:1.将点a点b连在一起;2.将当前状态退回到第k次操作时的状态;3.询问点a和点b是否连通。(n,操作数量<=200,000)

思路:可持久化并查集其实就是可持久化数组,我们用线段树代替数组,就只要打个可持久化线段树就可以了。路径压缩复杂度是均摊的不能可持久化,我们按秩合并,维护并查集每块的大小,连边把小的往大连,可以证明连出的最大深度为logn,总复杂度O(nlogn^2)。

#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 200000
#define ND 8000000
struct data{int f,s;};
struct node{int l,r;data x;}t[ND+5];
int n,rt[MN+5],tn,p;
int build(int l,int r)
{
    int p=++tn,mid=l+r>>1;
    if(l==r)t[p].x=(data){l,1};
    else t[p].l=build(l,mid),t[p].r=build(mid+1,r);
    return p;
}
data query(int k,int x)
{
    for(int l=1,r=n;;)
    {
        if(l==r)return t[k].x;
        int mid=l+r>>1;
        if(x>mid)k=t[k].r,l=mid+1;
        else k=t[k].l,r=mid;
    }
}
int change(int k,int l,int r,int x,data d)
{
    int p=++tn,mid=l+r>>1;
    if(l==r)t[p].x=d;
    else if(x>mid)t[p].l=t[k].l,t[p].r=change(t[k].r,mid+1,r,x,d);
    else t[p].l=change(t[k].l,l,mid,x,d),t[p].r=t[k].r;
    return p;
}
data gf(int k)
{
    data d=query(rt[p],k);
    return d.f==k?d:gf(d.f);
}
int main()
{
    int m,x,y,l=0;data a,b;
    n=read();m=read();
    rt[0]=build(1,n);
    for(p=1;p<=m;++p)
    {
        rt[p]=rt[p-1];x=read();y=read()^l;
        if(x==1)if((a=gf(y)).f!=(b=gf(read()^l)).f)
        {
            if(a.s>b.s)swap(a,b);
            rt[p]=change(rt[p],1,n,a.f,(data){b.f,a.s});
            rt[p]=change(rt[p],1,n,b.f,(data){b.f,a.s+b.s});
        }
        if(x==2)rt[p]=rt[y];
        if(x==3)printf("%d\n",l=(gf(y).f==gf(read()).f));
    }
}

 

posted on 2017-03-22 18:15  ditoly  阅读(168)  评论(0编辑  收藏  举报