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/+
+++++++++++++++++++++++++++++++++++++++++++