bzoj3674: 可持久化并查集

用可持久化线段树维护可持久化并查集。

调了一下午,改为按秩合并就过了。。。

没路径压缩的:

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=200100;
const int INF=1e9+10;

int n,m;
int fa[maxn];
int u,v,op;
int rk[maxn];

int bug=0;

struct SegTree
{
    int l,r;
    int ls,rs;
    int sum;
    void debug()
    {
        printf("l=%2d r=%2d ls=%2d rs=%2d sum=%2d\n",l,r,ls,rs,sum);
    }
};SegTree T[maxn*64];
int root[maxn],tot;

void push_up(int rt)
{
    T[rt].sum=T[T[rt].ls].sum+T[T[rt].rs].sum;
}

int build(int l,int r)
{
    int k=++tot;
    T[k].l=l;T[k].r=r;
    T[k].ls=-1;T[k].rs=-1;
    T[k].sum=0;
    if(l==r){
        T[k].sum=fa[l];
        return k;
    }
    int m=(l+r)>>1;
    T[k].ls=build(l,m);
    T[k].rs=build(m+1,r);
    push_up(k);
    return k;
}

int update(int rt,int p,int c)
{
    int k=++tot;
    T[k]=T[rt];
    if(T[rt].l==T[rt].r){
        T[k].sum=c;
        return k;
    }
    int m=(T[rt].l+T[rt].r)>>1;
    if(p<=m) T[k].ls=update(T[rt].ls,p,c);
    else T[k].rs=update(T[rt].rs,p,c);
    push_up(k);
    return k;
}

int query(int rt,int p)
{
    if(T[rt].l==T[rt].r) return T[rt].sum;
    int m=(T[rt].l+T[rt].r)>>1;
    if(p<=m) return query(T[rt].ls,p);
    else return query(T[rt].rs,p);
}

int find(int k,int x)
{
    int y=query(root[k],x);
    if(y==x) return x;
    else{
        int f=find(k,y);
        //update(root[k],x,f);  ///似乎不能路径压缩
        return f;
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        REP(i,1,n) fa[i]=i,rk[i]=1;
        root[0]=build(1,n);
        int las=0;
        REP(i,1,m){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d",&u,&v);
                u^=las;v^=las;
                int x=find(i-1,u),y=find(i-1,v);
                if(rk[x]>rk[y]) swap(x,y);
                if(x!=y) root[i]=update(root[i-1],x,y),rk[y]+=rk[x],rk[x]=0;
                else root[i]=root[i-1];
            }
            else if(op==2){
                scanf("%d",&u);
                u^=las;
                root[i]=root[u];
            }
            else{
                scanf("%d%d",&u,&v);
                u^=las;v^=las;
                int x=find(i-1,u),y=find(i-1,v);
                if(x==y) las=1;
                else las=0;
                root[i]=root[i-1];
                printf("%d\n",las);
            }
        }
    }
    return 0;
}
View Code

路径压缩(不一定正确,但是莫名其妙地过了...)

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=200100;
const int INF=1e9+10;

int n,m;
int fa[maxn];
int u,v,op;
int rk[maxn];

int bug=0;

struct SegTree
{
    int l,r;
    int ls,rs;
    int sum;
    void debug()
    {
        printf("l=%2d r=%2d ls=%2d rs=%2d sum=%2d\n",l,r,ls,rs,sum);
    }
};SegTree T[maxn*64];
int root[maxn],tot;

void push_up(int rt)
{
    T[rt].sum=T[T[rt].ls].sum+T[T[rt].rs].sum;
}

int build(int l,int r)
{
    int k=++tot;
    T[k].l=l;T[k].r=r;
    T[k].ls=-1;T[k].rs=-1;
    T[k].sum=0;
    if(l==r){
        T[k].sum=fa[l];
        return k;
    }
    int m=(l+r)>>1;
    T[k].ls=build(l,m);
    T[k].rs=build(m+1,r);
    push_up(k);
    return k;
}

int update(int rt,int p,int c)
{
    int k=++tot;
    T[k]=T[rt];
    if(T[rt].l==T[rt].r){
        T[k].sum=c;
        return k;
    }
    int m=(T[rt].l+T[rt].r)>>1;
    if(p<=m) T[k].ls=update(T[rt].ls,p,c);
    else T[k].rs=update(T[rt].rs,p,c);
    push_up(k);
    return k;
}

int query(int rt,int p)
{
    if(T[rt].l==T[rt].r) return T[rt].sum;
    int m=(T[rt].l+T[rt].r)>>1;
    if(p<=m) return query(T[rt].ls,p);
    else return query(T[rt].rs,p);
}

int find(int k,int x)
{
    int y=query(root[k],x);
    if(y==x) return x;
    else{
        int f=find(k,y);
        update(root[k],x,f);
        return f;
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        REP(i,1,n) fa[i]=i,rk[i]=1;
        root[0]=build(1,n);
        int las=0;
        REP(i,1,m){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d",&u,&v);
                u^=las;v^=las;
                int x=find(i-1,u),y=find(i-1,v);
                if(rk[x]>rk[y]) swap(x,y);
                if(x!=y) root[i]=update(root[i-1],x,y),rk[y]+=rk[x],rk[x]=0;
                else root[i]=root[i-1];
            }
            else if(op==2){
                scanf("%d",&u);
                u^=las;
                root[i]=root[u];
            }
            else{
                scanf("%d%d",&u,&v);
                u^=las;v^=las;
                int x=find(i-1,u),y=find(i-1,v);
                if(x==y) las=1;
                else las=0;
                root[i]=root[i-1];
                printf("%d\n",las);
            }
        }
    }
    return 0;
}
View Code

update------

似乎发现了一个问题,这个可持久化并查集不路径压缩但是按秩合并的话每次查询复杂度logn(树的深度),而路径压缩的正确性还有待验证。。如果能路径压缩的话复杂度就是常数级别(接近o(1))。

update-----

又发现了一个问题。。按秩合并有两种,一种是按集合大小合并,另一种是按树的深度合并。

按深度合并只要设深度为h[maxn],将深度小的合并到深度大的,

然后合并时加一句 if(h[x]>h[y]) swap(x,y);  ...   h[y]=max(h[y],h[x]+1)即可。

你可能感兴趣的:(bzoj3674: 可持久化并查集)