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

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

       并查集维护的是一个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 10000005
using 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

你可能感兴趣的:(并查集,可持久化线段树,启发式合并,可持久化数据结构)