Data structure - persistent disjoint-set may be

Pre-knowledge

Persistable array

Brief introduction

First question to a template:

Persistable disjoint-set

Roughly meaning to write you a data structure, support

  1. Merging a, b where a set of

  2. Return to the k operations after state

  3. Discover a and b are not in the same collection inside

It can be seen removed Common disjoint-set than the second operation can be solved

Common disjoint-set is generally based on the array, but may persist disjoint-set is based on an array of durable

Saying the name of this thing sounds very high, in fact it is actually not very difficult to achieve (

Disjoint-set

I believe will write ordinary disjoint-set. . . But there is one thing more important

The most common and simple optimization check is set by rank and path compression merger (I am more vegetables is generally played only path compression), is optimized so that the two disjoint-set the approximate time complexity becomes constant, but can be long-lasting of disjoint-set path can not be compressed by. Since the path compression process will be modified fa array, the array of altered general also ok, but be persistent arrays each change will add a chain. Over time the entire search space and complexity will be set off, so we mainly use the idea of ​​the merger according to rank.

In fact, according to the rank merger, then it is not difficult, here are a common set of check and merge code according to rank:

void merge(int x,int y) {
	x=find(x);
	y=find(y);
	if(x==y) return ;
	if(dep[x]<dep[y])
		fa[x]=y;
	else {
		fa[y]=x;
		if(dep[x]==dep[y]) dep[x]++;
	}
}

In the time of the merger is only the depth dep from small to large depth of the merger, then the same depth, then on the special sentence after [x] ++ like, so you can ensure maximum tree height is log (n), which is not present chain.

Specific principles. . . On their own Baidu, anyway, it is not hard (and heuristic merge bit like?)

Persistable disjoint-set

Because our optimization is based on the rank of the merger, so we have to maintain two arrays can be persistent (fa and dep). Of course, since it is two arrays, we will open twice as much memory space:

struct node {
	int l,r,sum;
} t[maxn*40*2];

Then we opened two root marked by an array of arrays of different roots, this is equivalent to opening two persistent array, and then used to allocate memory plus counters and some of the topics which the rest of what

int n,m,tot,cnt,rootfa[maxn],rootdep[maxn];

Next, a disjoint-set starts with an operation of a given array assignment fa, fa [i] = i, so we can write persistence number which also function to build a complete this work

void build(int l,int r,int &now) {
	now=++cnt;
	if(l==r) {
		t[now].sum=++tot;
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,t[now].l);
	build(mid+1,r,t[now].r);
}

Tot here is defined above, is used to increment the leaf node , it should still be quite simple

Then there is a persistence of the board of the array, not repeat them here:

void modify(int l,int r,int ver,int &now,int pos,int num) {//ver指向历史版本,now指向当前节点
	t[now=++cnt]=t[ver];
	if(l==r) {
		t[now].sum=num;
		return;
	}
	int mid=(l+r)/2;
	if(pos<=mid) modify(l,mid,t[ver].l,t[now].l,pos,num);
	else modify(mid+1,r,t[ver].r,t[now].r,pos,num);
}
int query(int l,int r,int now,int pos) {
	if(l==r) return t[now].sum;
	int mid=(l+r)/2;
	if(pos<=mid) return query(l,mid,t[now].l,pos);
	else return query(mid+1,r,t[now].r,pos);
}

Then we find to write a function.

find the function itself is relatively simple, but be careful that we do not compress path. The reason has been talked about earlier, where it placed directly Code:

int find(int ver,int x) {
	int fx=query(1,n,rootfa[ver],x);
	return fx==x?x:find(ver,fx);
}

Then we say no hurry to merge functions, let's look at how to achieve 2 operation.

2 operation is returned to the k-th version, of course, if we have a root array, we can write:

rootfa[ver]=rootfa[x];
rootdep[ver]=rootdep[x];

ver points to the current version, we can simply copied directly to the value of x versions of the root, so they all point to the same tree a chairman, it is equivalent to the entire version is copied.

Then is the merge function, merge function is actually not difficult, according to the rank of general did as the combined code through a random change like:

void merge(int ver,int x,int y) {
	x=find(ver-1,x);
	y=find(ver-1,y);
	if(x==y) {
		rootfa[ver]=rootfa[ver-1];
		rootdep[ver]=rootdep[ver-1];
	} else {
		int depx=query(1,n,rootdep[ver-1],x);
		int depy=query(1,n,rootdep[ver-1],y);
		if(depx<depy) {
			modify(1,n,rootfa[ver-1],rootfa[ver],x,y);
			rootdep[ver]=rootdep[ver-1];
		} else if(depx>depy) {
			modify(1,n,rootfa[ver-1],rootfa[ver],y,x);
			rootdep[ver]=rootdep[ver-1];
		} else {
			modify(1,n,rootfa[ver-1],rootfa[ver],x,y);
			modify(1,n,rootdep[ver-1],rootdep[ver],y,depy+1);
		}
	}
}

To questions about why ver-1, because the wording personal code (many other dalao board inside not the problem) problem, before we run a program to merge (can be understood as you hit a breakpoint in the merge there, then program status at the time of the power failure at the pause), although at this time is ver point with a new version, but nothing in this version, so we merge function is needed is root [ver-1] pointed version value within, so pay attention to the merge function inside every time it points to modify and query historical versions should be ver-1 (anyway, whether we are modifying the query or returned last two behind root arrays). If you then is to pay attention to every dep has no effect on the array when it is imperative ver-1 version of the array to be synchronized to the dep ver version to go inside, on the contrary, if there are changes, then do not sync.

Similarly, we find the function would have been to ver-1, but because the query does not involve any change to the array, so before we find directly in back of the Main function :( AC code that is written inside)

rootfa[ver]=rootfa[ver-1];
rootdep[ver]=rootdep[ver-1];

Now you may have a doubt, since you can find so engaging, so why not merge so too do it?

So, we return to the template may persist inside the array can be found in the first line of which was already given a statement now + 1, and here now pass or references. If we write two words before the merge, we finally change is rootfa [ver + 2], which is definitely wrong, so we have to manually give ver-1 rather than copying the entire root

The following questions are given template AC codes:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct node {
	int l,r,sum;
} t[maxn*40*2];
int n,m,tot,cnt,rootfa[maxn],rootdep[maxn];
void build(int l,int r,int &now) {
	now=++cnt;
	if(l==r) {
		t[now].sum=++tot;
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,t[now].l);
	build(mid+1,r,t[now].r);
}
void modify(int l,int r,int ver,int &now,int pos,int num) {
	t[now=++cnt]=t[ver];
	if(l==r) {
		t[now].sum=num;
		return;
	}
	int mid=(l+r)/2;
	if(pos<=mid) modify(l,mid,t[ver].l,t[now].l,pos,num);
	else modify(mid+1,r,t[ver].r,t[now].r,pos,num);
}
int query(int l,int r,int now,int pos) {
	if(l==r) return t[now].sum;
	int mid=(l+r)/2;
	if(pos<=mid) return query(l,mid,t[now].l,pos);
	else return query(mid+1,r,t[now].r,pos);
}
int find(int ver,int x) {
	int fx=query(1,n,rootfa[ver],x);
	return fx==x?x:find(ver,fx);
}
void merge(int ver,int x,int y) {
	x=find(ver-1,x);
	y=find(ver-1,y);
	if(x==y) {
		rootfa[ver]=rootfa[ver-1];
		rootdep[ver]=rootdep[ver-1];
	} else {
		int depx=query(1,n,rootdep[ver-1],x);
		int depy=query(1,n,rootdep[ver-1],y);
		if(depx<depy) {
			modify(1,n,rootfa[ver-1],rootfa[ver],x,y);
			rootdep[ver]=rootdep[ver-1];
		} else if(depx>depy) {
			modify(1,n,rootfa[ver-1],rootfa[ver],y,x);
			rootdep[ver]=rootdep[ver-1];
		} else {
			modify(1,n,rootfa[ver-1],rootfa[ver],x,y);
			modify(1,n,rootdep[ver-1],rootdep[ver],y,depy+1);
		}
	}
}
int main(void) {
	scanf("%d %d",&n,&m);
	build(1,n,rootfa[0]);
	for(int ver=1; ver<=m; ver++) {
		int opt,x,y;
		scanf("%d",&opt);
		if(opt==1) {
			scanf("%d %d",&x,&y);
			merge(ver,x,y);
		} else if(opt==2) {
			scanf("%d",&x);
			rootfa[ver]=rootfa[x];
			rootdep[ver]=rootdep[x];
		} else {
			scanf("%d %d",&x,&y);
			rootfa[ver]=rootfa[ver-1];
			rootdep[ver]=rootdep[ver-1];
			int fx=find(ver,x),fy=find(ver,y);
			printf("%d\n",fx==fy?1:0);
		}
	}
}

BTW, if you want to maintain the collection of property (for example, a collection of size and the like?), We must be persistent with the right to use the disjoint-set

In fact, very simple, that can persist inside the array to open a new variable to maintain

Guess you like

Origin www.cnblogs.com/jrdxy/p/12584745.html