并查集

Feature


To manage the elements grouping, we adopt this kind of special data structure. And it's based on the tree data structure. There are two main operations.

  • query whether \(a\) and \(b\) are in the same group.
  • merge the group of \(a\) and \(b\).

OJ Practise


poj 1182

Reference1 挑战程序设计竞赛 p87


#include <iostream>
#include <cstdio>
#include <cstring>

const int maxn= 50000*3+5;

int par[maxn];
int rank[maxn];    // par represents the father nodes, and rank indicate how to merge two sets, higher rank means father

void Init(int n)
{
    for (int i= 0; i< n; ++i){
        par[i]= i;
    }
    memset(rank, 0, maxn*sizeof(int));
}
int Find(int x)
{
    if (par[x]== x)
        return x;
    return par[x]= Find(par[x]);
}
void Merge(int x, int y)
{
    x= Find(x);
    y= Find(y);
    if (x== y)
        return;

    if (rank[x]< rank[y]){
        par[x]= y; 
    }
    else{
        par[y]= x;
        if (rank[x]== rank[y]){
            rank[x]++;
        }
    }
}
bool Same(int x, int y)
{
    return Find(x)== Find(y);
}

int main()
{
    int n, k, x, y, d;
    int ans= 0;

    scanf("%d %d", &n, &k);
    Init(3*n);

    while (k--){
        scanf("%d %d %d", &d, &x, &y);
        if (x<1 || x>n || y<1 || y>n){
            ++ans;
            continue;
        }
        x-= 1;
        y-= 1;
        if (1== d){
            if (Same(x, y+n) || Same(x, y+2*n)){
                ++ans;
            }
            else{
                Merge(x, y);
                Merge(x+n, y+n);
                Merge(x+2*n, y+2*n);
            }
        }
        else{
            if (Same(x, y) || Same(x, y+2*n)){
                ++ans;
            }
            else{
                Merge(x, y+n);
                Merge(x+n, y+2*n);
                Merge(x+2*n, y);
            }
        }
    }

    printf("%d\n", ans);

    return 0;
}

Reference2 hzwer


however this code TLE 😉...

#include <iostream>
#include <cstdio>
#define ll long long

using namespace std;

const int maxn= 50005;

int n, m, ans;
int d[maxn], fa[maxn];  
// Attention, d is the key array, it represents the
//  relation with its father node

/**
 * Module In Algorithm Competition
 */
int Read()
{
	ll x= 0, f= 1; 
	char ch= getchar();
	while ('0'>ch || '9'< ch){
		if ('-'== ch){
			f= -1; 
			ch= getchar();
		}
	}
	while (ch>= '0' && ch<= '9'){
		x= x*10+ ch-'0';
		ch= getchar();
	}

	return x*f;
}

int Find(int x)
{
	if (x== fa[x]){
		return x;
	}
	int t= fa[x];
	fa[x]= Find(fa[x]);
	d[x]= (d[x]+d[t])%3;  
	// d[fa[x]] has changed because we call the Find(fa[x]) recursively
	return fa[x];
}
void Merge(int x, int y, int f)
{
	int fx= Find(x), fy= Find(y);
	fa[fx]= fy;
	d[fx]= (d[y]-f-d[x]+3)%3;
	// think this use the vectors' addition and substraction
}

int main()
{
	n= Read();
	m= Read();
	int f, u, v;
	for (int i= 1; i<= n; ++i)
		fa[i]= i;
	ans= 0;

	while (m--){
		f= Read();
		u= Read();
		v= Read();

		if (u>n || v>n || (f==2 && u==v)){
			++ans;
		}
		else{
			if (Find(u)== Find(v)){
				if (1== f && d[u]!= d[v]){
					++ans;
				}
				if (2==f && (d[u]+1)%3 != d[v]){
					++ans;
				}
			}
			else{
				Merge(u, v, f-1);
			}
		}
	}
	printf("%d\n", ans);

	return 0;
}

ZOJ 3261


This problem is quite different, because Union-Find Set doesn't support delete operation. And here is a reall amazing trick:Offline Storage, Reverse processing. Really funny!!!! But there are lots of details need to be noticed, I'm a stupid Ne...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <utility>
#include <map>
#include <algorithm>
using namespace std;

const int maxn= 1e5+3;
const int maxq= 5e5+3;

int p[maxn], par[maxn];
map<pair<int, int>, int> bd;
pair<int, int> dis[maxq];
char req[maxq], odr[10];
int ans[maxq];
int Q[maxq];

void Init()
{
	memset(p, 0, sizeof(p));
	memset(par, 0, sizeof(par));
	memset(req, 0, sizeof(req));
	memset(ans, 0, sizeof(ans));
	memset(Q, 0, sizeof(Q));
	memset(dis, 0, sizeof(dis));
	bd.clear();
}
int Find(int idx)
{
	if (idx== par[idx]){
		return idx;
	}
	return par[idx]= Find(par[idx]);
}
void Union(int x, int y)
{
	x= Find(x);
	y= Find(y);

	if (x== y){
		return;
	}


	if (p[x]> p[y] || (p[x]== p[y] && x<y)){
		par[y]= x;
	}
	else{
		par[x]= y;
	}
}

int main()
{
	int n, m, q, a, b;
	int fst= 1;
	while (EOF!= scanf("%d", &n)){
		Init();
		if (fst){
			fst= 0;
		}
		else{
			cout<<endl;
		}
		for (int i= 0; i< n; ++i){
			scanf("%d", p+i);
			par[i]= i;
		}
		scanf("%d", &m);
		for (int i= 0; i< m; ++i){
			scanf("%d %d", &a, &b);
			if (a> b){
				swap(a, b);		//important!!!! I'm so dumb...
			}
			bd[pair<int, int>(a, b)]= 1;
		}
		scanf("%d", &q);
		int ida= 0, idd= 0;
		for (int i= 0; i< q; ++i){
			cin>>odr;
			req[i]= odr[0];
			if ('d'== req[i]){
				scanf("%d %d", &a, &b);
				if (a> b){
					swap(a, b);
				}
				dis[idd++]= pair<int, int>(a, b);
				bd[pair<int, int>(a, b)]= 0;
			}
			else{
				scanf("%d", &a);
				ans[ida++]= a;
			}
		}
		int len= ida;
		for (map<pair<int, int>, int>:: iterator it= bd.begin(); bd.end()!= it; ++it){
			if (it->second){
				pair<int, int> tmp= it->first;
				Union(tmp.first, tmp.second);
			}
		}
		for (int i= q-1; 0<= i; --i){
			if ('d'== req[i]){
				pair<int, int> tmp= dis[--idd];
				Union(tmp.first, tmp.second);
			}
			else{
				int tmp= ans[--ida];
				int fa= Find(tmp);
				if (p[fa]> p[tmp]){
					ans[ida]= fa;
				}
				else{
					ans[ida]= -1;
				}
			}
		}
		for (int i= 0; i< len; ++i){
			printf("%d\n", ans[i]);
		}
	}

	return 0;
}

POJ 2524


Easy...

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn= 5e5+3;

int par[maxn];
int rnk[maxn];

void Init(const int n)
{
	memset(rnk, 0, sizeof(rnk));
	for (int j= 0; j< n; ++j){
		par[j]= j;
	}
}
int Find(int sn)
{
	if (par[sn]== sn){
		return sn;
	}
	return par[sn]= Find(par[sn]);
}
bool Same(int x, int y)
{
	return Find(x)== Find(y);
}
bool Union(int x, int y)
{
	x= Find(x);
	y= Find(y);

	if (x== y){
		return false;
	}
	if (rnk[x]< rnk[y]){
		par[x]= y;
	}
	else{
		par[y]= x;
		if (rnk[x]== rnk[y]){
			++rnk[x];
		}
	}

	return true;
}

int main()
{
	int n, m, kase= 0;

	while (EOF!= scanf("%d %d", &n, &m) && (n || m)){
		int a, b;
		Init(n);
		for (int i= 0; i< m; ++i){
			scanf("%d %d", &a, &b);
			if (Union(a, b)){
				--n;
			}
		}

		printf("Case %d: %d\n", ++kase, n);
	}

	return 0;
}

POJ 2236

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn= 1e3+5;

struct Point{
	int x, y;
	int par, rnk;
	int Dis2(const Point &b) const
	{
		return (x-b.x)*(x-b.x)+(y-b.y)*(y-b.y);
	}
}Wifi[maxn];
bool rep[maxn];
int d;

void Init(const int N)
{
	memset(Wifi, 0, sizeof(Wifi));
	memset(rep, 0, sizeof(rep));
	for (int i= 1; i<= N; ++i){
		Wifi[i].par= i;
	}
}
int Find(int sn)
{
	if (sn== Wifi[sn].par){
		return sn;
	}
	return Wifi[sn].par= Find(Wifi[sn].par);
}
void Union(int a, int b)
{
	a= Find(a);
	b= Find(b);

	if (a== b){
		return;
	}

	if (Wifi[a].rnk< Wifi[b].rnk){
		Wifi[a].par= b;
	}
	else{
		Wifi[b].par= a;
		if (Wifi[a].rnk== Wifi[b].rnk){
			++Wifi[a].rnk;
		}
	}
}

int main()
{
	int N;
	int pc, pc1;
	char opr;

	scanf("%d %d", &N, &d);
	Init(N);
	for (int i= 1; i<= N; ++i){
		scanf("%d %d", &Wifi[i].x, &Wifi[i].y);
	}

	while (EOF!= scanf(" %c", &opr)){
		if ('O'== opr){
			scanf("%d", &pc);
			rep[pc]= true;

			for (int i= 1; i<= N; ++i){
				if (rep[i] && (d*d)>= Wifi[pc].Dis2(Wifi[i])){
					Union(i, pc);
				}
			}
		}
		else{
			scanf("%d %d", &pc, &pc1);
			if (Find(pc)== Find(pc1)){
				cout<<"SUCCESS"<<endl;
			}
			else{
				cout<<"FAIL"<<endl;
			}
		}
	}

	return 0;
}
posted @ 2019-12-09 17:13  IdiotNe  阅读(90)  评论(0)    收藏  举报