【种类并查集】P5937 [CEOI1999]Parity Game

 P5937 [CEOI1999]Parity Game

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stdlib.h>
#include <math.h>
#define  LL long long
using namespace std;
//种类并查集(也叫扩展域)
//本质是两种互斥关系.如本题就要记录与自己相同的和与自己不同的,就将 f 数组开大到原来的两倍,
//用 f[i] 表示与自己奇偶性相同的集合,用 f[i+n] 表示与 i 奇偶性不同的元素集合。
int n, m, tot = 0;
struct fp {
	int u, v, w;
}a[10010];//直接存边法,记录u到v两点之间的1的奇数偶数
int f[200010], aa[200010];//f记录父亲节点,因为是种类并查集,所以要开到总数的两倍.aa数组是为了离散化
int find(int x)//普通的并查集查询
{
	if (f[x] == x) return x;
	return f[x] = find(f[x]);
}


int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m;//输入长度和问题个数
	char t[6];//记录odd和even
	for (int i = 1; i <= m; i++) {
		cin >> a[i].u >> a[i].v >> t;
		a[i].u--;//因为记录时要记录u,v本身.比如5到9之间包括本身,所以5到9之间有5个数,5 要减去1
		aa[++tot] = a[i].u;//离散化标准步骤,先将所有点存到一个副本里面如aa
		aa[++tot] = a[i].v;
		if (t[0] == 'o') {
			a[i].w = 1;//记录奇偶,1为奇数,0为偶数
		}
		else a[i].w = 0;
	}
	sort(aa + 1, aa + tot + 1);//将副本的数字排序确定大小关系
	tot = unique(aa + 1, aa + tot + 1) - aa - 1;//统计去重后的个数注意aa是从1还是0开始存储的.从1存储要减去1
	for (int i = 0; i <= tot * 2; i++) f[i] = i;//并查集的初始化
	for (int i = 1, x, y; i <= m; i++) {
		x = lower_bound(aa + 1, aa + tot + 1, a[i].u) - aa;//二分法查询,用aa中a.u的大小排序后的位置代替a.u,减小了数据数的大小的同时确保各个数字之间的
		//大小关系不变
		y = lower_bound(aa + 1, aa + tot + 1, a[i].v) - aa;
		if (a[i].w == 0) {//如果是偶数//那么从1开始到x的奇偶应该与到y的奇偶相同,9-3=6偶,8-4=4偶
			if (find(x) == find(y + tot) || find(x + tot) == find(y)) {//那么因为f[i] 表示与自己奇偶性相同的集合,用 f[i+n] 表示与 i 奇偶性不同的元素集合。
				//所以两者应该查找到的祖先应该不同,因为x在奇偶性相同的集合中查找到的父亲与y在加tot以后查找到的奇偶不同的集合一致那么就矛盾了
				cout << i - 1;
				exit(0);
			}
			f[find(x + tot)] = find(y + tot);//表示与x的奇偶性不同的便是与y奇偶性不同的集合,两者同属一个
			f[find(x)] = find(y);//
		}
		else {
			if (find(x) == find(y) || find(x + tot) == find(y + tot)) {//如果是奇数那么,两则的奇偶性集合(父亲)应该不相同,
				cout << i - 1;
				exit(0);
			}
			f[find(x)] = find(y + tot);
			f[find(x + tot)] = find(y);
		}
	}

	cout << m;

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值