bzoj3674: 可持久化并查集
用可持久化线段树维护可持久化并查集。
调了一下午,改为按秩合并就过了。。。
没路径压缩的:
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200100; const int INF=1e9+10; int n,m; int fa[maxn]; int u,v,op; int rk[maxn]; int bug=0; struct SegTree { int l,r; int ls,rs; int sum; void debug() { printf("l=%2d r=%2d ls=%2d rs=%2d sum=%2d\n",l,r,ls,rs,sum); } };SegTree T[maxn*64]; int root[maxn],tot; void push_up(int rt) { T[rt].sum=T[T[rt].ls].sum+T[T[rt].rs].sum; } int build(int l,int r) { int k=++tot; T[k].l=l;T[k].r=r; T[k].ls=-1;T[k].rs=-1; T[k].sum=0; if(l==r){ T[k].sum=fa[l]; return k; } int m=(l+r)>>1; T[k].ls=build(l,m); T[k].rs=build(m+1,r); push_up(k); return k; } int update(int rt,int p,int c) { int k=++tot; T[k]=T[rt]; if(T[rt].l==T[rt].r){ T[k].sum=c; return k; } int m=(T[rt].l+T[rt].r)>>1; if(p<=m) T[k].ls=update(T[rt].ls,p,c); else T[k].rs=update(T[rt].rs,p,c); push_up(k); return k; } int query(int rt,int p) { if(T[rt].l==T[rt].r) return T[rt].sum; int m=(T[rt].l+T[rt].r)>>1; if(p<=m) return query(T[rt].ls,p); else return query(T[rt].rs,p); } int find(int k,int x) { int y=query(root[k],x); if(y==x) return x; else{ int f=find(k,y); //update(root[k],x,f); ///似乎不能路径压缩 return f; } } int main() { freopen("in.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ REP(i,1,n) fa[i]=i,rk[i]=1; root[0]=build(1,n); int las=0; REP(i,1,m){ scanf("%d",&op); if(op==1){ scanf("%d%d",&u,&v); u^=las;v^=las; int x=find(i-1,u),y=find(i-1,v); if(rk[x]>rk[y]) swap(x,y); if(x!=y) root[i]=update(root[i-1],x,y),rk[y]+=rk[x],rk[x]=0; else root[i]=root[i-1]; } else if(op==2){ scanf("%d",&u); u^=las; root[i]=root[u]; } else{ scanf("%d%d",&u,&v); u^=las;v^=las; int x=find(i-1,u),y=find(i-1,v); if(x==y) las=1; else las=0; root[i]=root[i-1]; printf("%d\n",las); } } } return 0; }
路径压缩(不一定正确,但是莫名其妙地过了...)
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200100; const int INF=1e9+10; int n,m; int fa[maxn]; int u,v,op; int rk[maxn]; int bug=0; struct SegTree { int l,r; int ls,rs; int sum; void debug() { printf("l=%2d r=%2d ls=%2d rs=%2d sum=%2d\n",l,r,ls,rs,sum); } };SegTree T[maxn*64]; int root[maxn],tot; void push_up(int rt) { T[rt].sum=T[T[rt].ls].sum+T[T[rt].rs].sum; } int build(int l,int r) { int k=++tot; T[k].l=l;T[k].r=r; T[k].ls=-1;T[k].rs=-1; T[k].sum=0; if(l==r){ T[k].sum=fa[l]; return k; } int m=(l+r)>>1; T[k].ls=build(l,m); T[k].rs=build(m+1,r); push_up(k); return k; } int update(int rt,int p,int c) { int k=++tot; T[k]=T[rt]; if(T[rt].l==T[rt].r){ T[k].sum=c; return k; } int m=(T[rt].l+T[rt].r)>>1; if(p<=m) T[k].ls=update(T[rt].ls,p,c); else T[k].rs=update(T[rt].rs,p,c); push_up(k); return k; } int query(int rt,int p) { if(T[rt].l==T[rt].r) return T[rt].sum; int m=(T[rt].l+T[rt].r)>>1; if(p<=m) return query(T[rt].ls,p); else return query(T[rt].rs,p); } int find(int k,int x) { int y=query(root[k],x); if(y==x) return x; else{ int f=find(k,y); update(root[k],x,f); return f; } } int main() { freopen("in.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ REP(i,1,n) fa[i]=i,rk[i]=1; root[0]=build(1,n); int las=0; REP(i,1,m){ scanf("%d",&op); if(op==1){ scanf("%d%d",&u,&v); u^=las;v^=las; int x=find(i-1,u),y=find(i-1,v); if(rk[x]>rk[y]) swap(x,y); if(x!=y) root[i]=update(root[i-1],x,y),rk[y]+=rk[x],rk[x]=0; else root[i]=root[i-1]; } else if(op==2){ scanf("%d",&u); u^=las; root[i]=root[u]; } else{ scanf("%d%d",&u,&v); u^=las;v^=las; int x=find(i-1,u),y=find(i-1,v); if(x==y) las=1; else las=0; root[i]=root[i-1]; printf("%d\n",las); } } } return 0; }
update------
似乎发现了一个问题,这个可持久化并查集不路径压缩但是按秩合并的话每次查询复杂度logn(树的深度),而路径压缩的正确性还有待验证。。如果能路径压缩的话复杂度就是常数级别(接近o(1))。
update-----
又发现了一个问题。。按秩合并有两种,一种是按集合大小合并,另一种是按树的深度合并。
按深度合并只要设深度为h[maxn],将深度小的合并到深度大的,
然后合并时加一句 if(h[x]>h[y]) swap(x,y); ... h[y]=max(h[y],h[x]+1)即可。
没有AC不了的题,只有不努力的ACMER!