AcWing 239 奇偶游戏 (带权并查集 / 扩展域并查集)

题目链接:https://www.acwing.com/problem/content/description/241/

并查集的一个重要功能就是动态维护具有传递性的关系,
比如:相等,奇偶性等等

如果传递关系只有一种,比如相等,那就使用普通的并查集来维护即可

如果传递关系不只有一种,比如本题的奇偶性:

  1. 如果 (x_1)(x_2) 奇偶性相同, (x_2)(x_3) 奇偶性相同,则 (x_1)(x_3) 奇偶性相同(与相等关系一样)
  2. 如果 (x_1)(x_2) 奇偶性相同, (x_2)(x_3) 奇偶性不同,则 (x_1)(x_3) 奇偶性不同
  3. 如果 (x_1)(x_2) 奇偶性不同, (x_2)(x_3) 奇偶性不同,则 (x_1)(x_3) 奇偶性相同

这时候需要使用边带权的并查集:
(d[x]) 表示 (x) 与集合的根的奇偶性关系,(0) 相同,(1) 不同
(find) 的时候更新 (d) 即可,合并时也要更新一下

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 30010; 

int n, m;

struct Q{
	int l, r, ans;
}query[maxn];

char s[10];

int a[maxn], cnt = 0;

int fa[maxn], d[maxn]; // 0 same  1 different

int find(int x){
	if(fa[x] == x) return x;
	int root = find(fa[x]); 
	d[x] ^= d[fa[x]];
	return fa[x] = root;
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i){
		scanf("%d%d%s", &query[i].l, &query[i].r, s);
		if(s[0] == 'o') query[i].ans = 1; else query[i].ans = 0;
		a[++cnt] = query[i].r, a[++cnt] = query[i].l - 1;
	}
	
	sort(a + 1, a + 1 + cnt);
	
	n = unique(a + 1, a + 1 + cnt) - a - 1; 
	
	for(int i = 1; i <= n ; ++i) fa[i] = i;
	
	for(int i = 1; i <= m; ++i){
		int x = lower_bound(a + 1, a + 1 + n, query[i].l - 1) - a;
		int y = lower_bound(a + 1, a + 1 + n, query[i].r) - a; 		

		int p = find(x), q = find(y); 
		if(p == q) {
			if((d[x] ^ d[y]) != query[i].ans){
				printf("%d
",i - 1); return 0;
			}
		} else{
			fa[p] = q;
			d[p] = d[x] ^ d[y] ^ query[i].ans;
		}
	} 
	printf("%d
",m);
	
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/13956574.html