bzoj 3673: 可持久化并查集 by zky
bzoj 3673: 可持久化并查集 by zky
Time Limit: 5 Sec Memory Limit: 128 MBDescription
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
0<n,m<=2*10^4
Sample Input
5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
Sample Output
1
0
1
0
1
主席树+启发式合并+并查集
以节点编号为下标,以祖先节点为权值建立主席树(也就是主席树内维护祖先节点是谁)
并查集本就是支持合并的
所谓启发式合并就是按秩合并
即朴素的并查集合并时,谁合并谁是随机的
按秩合并,秩即树的深度,深度小的树合并到深度大的树的上面
所以我们要维护一个deep[]数组,记录每个集合的祖先节点的深度(根节点到最深的叶节点的距离)
并查集合并时找到祖先节点,所以只有祖先节点的深度是有效的
那么显然,只有合并两颗深度相同的树时才会更新deep
查找祖先节点查找的比较巧妙
利用主席树维护每个时刻的祖先节点
将朴素的直接查询,改成先到主席树中找到这个节点编号的位置,返回这个位置存储的father
对于回到历史版本,只需令主席树中起指示节点所在线段树位置的root[]=历史版本的root即可
#include<cstdio> #include<algorithm> using namespace std; int n,m,cnt; int root[20001*20],lc[20001*20],rc[20001*20],fa[20001*20],deep[20001*20]; void build(int & k,int l,int r) { k=++cnt; if(l==r) {fa[k]=l;return;} int mid=l+r>>1; build(lc[k],l,mid); build(rc[k],mid+1,r); } void change(int pre,int & now,int l,int r,int x,int y) { now=++cnt; if(l==r) { fa[now]=x; deep[now]=deep[pre]; return; } int mid=l+r>>1; if(y<=mid) { rc[now]=rc[pre]; change(lc[pre],lc[now],l,mid,x,y); } else { lc[now]=lc[pre]; change(rc[pre],rc[now],mid+1,r,x,y); } } int query(int y,int l,int r,int w) { if(l==r) return fa[y]; int mid=l+r>>1; if(w<=mid) return query(lc[y],l,mid,w); else return query(rc[y],mid+1,r,w); } int find(int x,int i) { int p=query(root[i],1,n,x); if(p==x) return x; return find(p,i); } void update(int x,int l,int r,int w) { if(l==r) {deep[x]++;return;} int mid=l+r>>1; if(w<=mid) update(lc[x],l,mid,w); else update(rc[x],mid+1,r,w); } void unionn(int x,int y,int i) { if(deep[x]<deep[y]) swap(x,y); change(root[i-1],root[i],1,n,x,y); if(deep[x]==deep[y]) update(root[x],1,n,x); } int main() { scanf("%d%d",&n,&m); build(root[0],1,n); int x,y,z; for(int i=1;i<=m;i++) { scanf("%d",&z); if(z==1) { root[i]=root[i-1]; scanf("%d%d",&x,&y); x=find(x,i);y=find(y,i); unionn(x,y,i); } else if(z==2) { scanf("%d",&x);root[i]=root[x]; } else { scanf("%d%d",&x,&y); root[i]=root[i-1]; x=find(x,i);y=find(y,i); printf("%d\n",x==y); } } }