bzoj3673&3674 可持久化并查集 可持久化线段树

来源:互联网 发布:高性能网络编程 陶辉 编辑:程序博客网 时间:2024/04/30 10:37

        实际上,我们可以把并查集问题转化为维护一个数组单点修改的问题,因为:

       并查集维护的是一个fa[]数组,表示x的父亲,那么我们就来维护这个fa[]数组。由于存在若干修改和历史版本,实际上我们令fa[x]表示节点x(x为在线段树中的动态添加的节点)的父亲在原数组(下表1..n)中的位置。对于3中操作:

       1.合并:用类似并查集的方式找到x,y的根u,v,然后采用启发式合并(这样只需要修改一个),对每一个节点维护一个秩,每次将秩小的并到大的,相等的话根的秩+1。

       2.恢复到历史版本:rt指过去就好了;

       3.查询:找根比较的好了。

AC代码如下(bzoj3674的):

#include<iostream>#include<cstdio>#include<cstring>#define N 10000005using namespace std;int n,m,trtot,rt[200005],ls[N],rs[N],fa[N],dep[N];int read(){int x=0; char ch=getchar();while (ch<'0' || ch>'9') ch=getchar();while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }return x;}void build(int &k,int l,int r){k=++trtot; int mid=(l+r)>>1;if (l==r){ fa[k]=l; return; }build(ls[k],l,mid); build(rs[k],mid+1,r);}void ins(int l,int r,int x,int &y,int k,int v){y=++trtot; int mid=(l+r)>>1;if (l==r){ fa[y]=v; return; }if (k<=mid){ rs[y]=rs[x]; ins(l,mid,ls[x],ls[y],k,v); }else{ ls[y]=ls[x]; ins(mid+1,r,rs[x],rs[y],k,v); }}void updata(int l,int r,int k,int x){if (l==r){ dep[k]++; return; } int mid=(l+r)>>1;if (x<=mid) updata(l,mid,ls[k],x);else updata(mid+1,r,rs[k],x);}int qry(int k,int l,int r,int x){if (l==r) return k; int mid=(l+r)>>1;if (x<=mid) return qry(ls[k],l,mid,x);else return qry(rs[k],mid+1,r,x);}int getfa(int k,int x){int t=qry(k,1,n,x);return (fa[t]==x)?t:getfa(k,fa[t]);}int main(){n=read(); m=read(); int i,ans=0;build(rt[0],1,n);for (i=1; i<=m; i++){int k=read(),x,y;if (k==1){x=read()^ans; y=read()^ans;rt[i]=rt[i-1];x=getfa(rt[i],x); y=getfa(rt[i],y);if (fa[x]!=fa[y]){if (dep[x]>dep[y]) swap(x,y);ins(1,n,rt[i],rt[i],fa[x],fa[y]);if (dep[x]==dep[y]) updata(1,n,rt[i],fa[y]);}} else if (k==2) rt[i]=rt[read()^ans]; else{int x=read()^ans,y=read()^ans;rt[i]=rt[i-1];x=getfa(rt[i],x); y=getfa(rt[i],y);printf("%d\n",(fa[x]==fa[y])?ans=1:ans=0);}}return 0;}


by lych

2016.2.22

0 0