BZOJ 3673: 可持久化并查集(可持久化并查集+启发式合并)
http://www.lydsy.com/JudgeOnline/problem.php?id=3673
思路:
可持久化数组可以用可持久化线段树来实现,并查集的查询操作和原来的一般并查集操作是差不多的,只不过是在线段树上操作。需要注意的是并查集的合并,需要按秩来进行启发式合并。
#include<iostream> #include<cstdio> using namespace std; const int maxn = 2*10000+5; int n,m,tot; int t[maxn*50],h[maxn*50]; struct node { int l,r,fa; }a[maxn*50]; int build(int l, int r) { int root = ++tot; if(l==r) {a[root].fa = l;return root;} int mid = (l+r)>>1; a[root].l = build(l,mid); a[root].r = build(mid+1,r); return root; } int query(int root, int x, int l, int r) { if(l==r) return root; int mid = (l+r)>>1; if(x<=mid) return query(a[root].l,x,l,mid); else return query(a[root].r,x,mid+1,r); } int finds(int root, int x) { int p = query(root,x,1,n); if(x == a[p].fa) return p; else return finds(root,a[p].fa); } int modify(int pre, int x, int y, int l, int r) { int root = ++tot; if(l==r) { a[root].fa = y; h[root] = h[pre]; //这个不能忘 return root; } a[root].l = a[pre].l, a[root].r = a[pre].r; int mid = (l+r)>>1; if(x<=mid) a[root].l = modify(a[pre].l, x, y, l, mid); else a[root].r = modify(a[pre].r, x, y, mid+1, r); return root; } void update(int root, int x, int l, int r) { if(l==r) {h[root]++;return;} int mid = (l+r)>>1; if(x<=mid) update(a[root].l,x, l, mid); else update(a[root].r,x, mid+1, r); } void unions(int x, int y, int i) { if(h[x]>h[y]) swap(x,y); t[i] = modify(t[i-1],a[x].fa,a[y].fa,1,n); //因为上面的交换,所以这里用fa if(h[x]==h[y]) update(t[i],a[y].fa,1,n); //如果深度相等,则插入后深度会+1 } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); tot = 0; t[0] = build(1,n); for(int i=1;i<=m;i++) { int op; scanf("%d",&op); if(op==1) { int aa,bb; scanf("%d%d",&aa,&bb); t[i] = t[i-1]; int x = finds(t[i],aa); int y = finds(t[i],bb); if(a[x].fa != a[y].fa) unions(x,y,i); } if(op==2) { int aa; scanf("%d",&aa); t[i] = t[aa]; } if(op==3) { int aa,bb; scanf("%d%d",&aa,&bb); t[i] = t[i-1]; int x = finds(t[i],aa); int y = finds(t[i],bb); if(a[x].fa == a[y].fa) puts("1"); else puts("0"); } } return 0; }