BZOJ3673 & BZOJ3674 & 洛谷3402:可持久化并查集——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=3673

https://www.lydsy.com/JudgeOnline/problem.php?id=3674

https://www.luogu.org/problemnew/show/P3402

这里最强的应该是BZOJ3674,于是接下来讲的内容和代码与BZOJ3674相同而非另外两道题,但核心思维完全一致。

n个集合 m个操作

操作:

1 a b 合并a,b所在集合

2 k 回到第k次操作之后的状态(查询算作操作)

3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x= x xor lastans,lastans的初始值为0

可持久化的你没见过的数据结构,都按照可持久化线段树来做准没错。(flag)

用主席树记录历史版本,维护每个节点的爸爸和启发式合并所需要的并查集的深度。

这样对于2操作只需要把版本号改了即可(程序中是rt[i]=rt[k])。

原find操作可以暴力爬树一步步往上找,启发式合并深度为O(logn),主席树查询一次是O(logn),复杂度就是O(log^2n)。

原union操作就相当于主席树的insert操作,比如fa[u]=v,我们只需要主席树找到代表u的点u0,然后fa[u0]=v即可。

第三个操作有上面的基础就是傻逼操作了。

#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int N=200001;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct tree{
    int l,r;
}tr[N*20];
int rt[N],fa[N*20],dep[N*20],n,m,pool;
inline void build(int &x,int l,int r){
    x=++pool;
    if(l==r){
    fa[x]=l;
    return;
    }
    int mid=(l+r)>>1;
    build(tr[x].l,l,mid);
    build(tr[x].r,mid+1,r);
}
inline void insert(int y,int &x,int l,int r,int u,int v){
    tr[x=++pool]=tr[y];
    if(l==r){
    fa[x]=v;
    return;
    }
    int mid=(l+r)>>1;
    if(u<=mid)insert(tr[y].l,tr[x].l,l,mid,u,v);
    else insert(tr[y].r,tr[x].r,mid+1,r,u,v);
    return;
}
inline void add(int y,int &x,int l,int r,int p){
    if(l==r){
    dep[x]++;
    return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)add(tr[y].l,tr[x].l,l,mid,p);
    else add(tr[y].r,tr[x].r,mid+1,r,p);
    return;
}
inline int query(int k,int l,int r,int p){
    if(l==r)return k;
    int mid=(l+r)>>1;
    if(p<=mid)return query(tr[k].l,l,mid,p);
    else return query(tr[k].r,mid+1,r,p);
}
inline int find(int k,int x){
    while(233){
    int f=query(rt[k],1,n,x);
    if(x==fa[f])return f;
    x=fa[f];
    }
}
inline void unionn(int k,int u,int v){
    if(dep[u]>dep[v])swap(u,v);
    insert(rt[k],rt[k],1,n,fa[u],fa[v]);
    if(dep[u]==dep[v])add(rt[k],rt[k],1,n,fa[v]);
}
int main(){
    n=read(),m=read();
    build(rt[0],1,n);
    int ans=0;
    for(int i=1;i<=m;i++){
    int op=read();
    if(op==1){
        rt[i]=rt[i-1];
        int a=find(i,read()^ans),b=find(i,read()^ans);
        if(a!=b)unionn(i,a,b);
    }
    if(op==2){
        int k=read()^ans;rt[i]=rt[k];
    }
    if(op==3){
        rt[i]=rt[i-1];
        int a=find(i,read()^ans),b=find(i,read()^ans);
        printf("%d\n",ans=a==b?1:0);
    }
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-03-30 19:23  luyouqi233  阅读(232)  评论(0编辑  收藏  举报