Parity Game——种类并查集、权值并查集、离散化

news2024/9/24 16:31:55

题目描述

思路

怎么得到这个序列中每一段的关系?

  • 我们可以把这个只包含0和1的序列看作一个数组,0表示当前位置为0,1表示当前位置为1,利用前缀和的性质可以知道某一段中所包含的1的数量
  • sum1 = a[r] - a[l-1]
  1. 如果sum1为偶数,那么a[r] 和 a[l-1]的奇偶性相同
  2. 如果sum1为奇数,那么a[r] 和 a[l-1]的奇偶性不同
  • 找到它们之间的关系,我们就可以使用并查集来存储他们

为什么要离散化?

  • 序列的长度小于等于1e9,然而序列中下标出现的次数最多为1e4,所以使用离散化

种类并查集

  • 序列中某一段有两种关系,奇数个1、偶数个1
  • 我们定义两个扩展域,1~n表示偶数,n + 1 ~ 2 * n表示奇数
  • 每次知道一个关系之后,将偶数区域和奇数区域都进行处理,像枚举一样,不漏掉每一种情况
  • 并查集中存储的是区域与区域之间的关系,如果有一个关系成立,那么这个集合中其它的关系都成立

代码实现

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 1e4 + 10;

int fa[2 * N];		// 两个扩展域:1~N表示偶数关系,N+1~2N表示奇数关系
int n, m;
vector<int> ans;	// 离散化数组

struct node	// 结构体,存储每一段区间的左右端点
{
	int x, y;
	string op;
}a[N];

int get(int u)	// 二分查找,将原数据离散化成下标
{
	int l = 0, r = ans.size();
	while(l + 1 != r)
	{
		int mid = l + r >> 1;
		if(ans[mid] < u) l = mid;
		else r = mid;
	}
	return r;
}

int find(int u)		// 返回当前元素的祖宗元素,在哪个集合中
{
	if(fa[u] != u) fa[u] = find(fa[u]);
	return fa[u];
}

void merge(int x, int y)	// 将x合并到y所在的集合中
{
	fa[find(x)] = find(y);
}

int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
	{
		cin >> a[i].x >> a[i].y >> a[i].op;	
		a[i].x--;	// 因为利用了前缀和的性质,所以要将左端点-1,满足sum = a[r] - a[l-1]
		ans.push_back(a[i].x);
		ans.push_back(a[i].y);
	}
	sort(ans.begin(), ans.end());	// 将数据进行排序
	ans.erase(unique(ans.begin(), ans.end()), ans.end());	// 进行去重
	ans.insert(ans.begin(), 0);	// 将离散化之后的下标从1开始
	for(int i = 1;i <= m; i++)	// 找到每个区间左右端点离散化之后的数据,使用新数据
	{
		a[i].x = get(a[i].x);
		a[i].y = get(a[i].y);
	}
	for(int i = 0;i < 2 * N; i++) fa[i] = i;	// 初始化集合,每一个元素在不同的集合中
	n = ans.size();	// 每个扩展域的范围
	for(int i = 1; i <= m; i++)	// 开始遍历每一个回答,判断左右端点集合中的关系
	{
		int x = a[i].x, y = a[i].y;
		string op = a[i].op;
		if(op == "even")	// 有偶数个1
		{
            // 只需要判断一种情况就可,因为他们的关系是对称的
			if(find(x) == find(y + n))	// x为偶数的集合中,存在y为奇数的关系,矛盾
			{
				cout << i - 1 << endl;
				return 0;
			}
            // x与y的奇偶性相同
			merge(x, y); 	// 将x为偶数,y为偶数放在一个集合中
			merge(x + n, y + n);	// 将x为奇数,y为奇数放在一个集合中
		}
		else	// 有奇数个1,那么x和y的奇偶性不同
		{
			if(find(x) == find(y))	// x为偶数的集合中,存在y为偶数的关系,矛盾
			{
				cout << i - 1 << endl;
				return 0;
			}
			merge(x, y + n);	// 将x为偶数,y为奇数放在一个集合中
			merge(x + n, y);	// 将x为奇数,y为偶数放在一个集合中
		}
	}
	cout << m << endl;
	
	return 0;
}

权值并查集

  • 每一个区间的奇偶性有两种情况,奇数或者偶数
  • 我们可以维护集合中每个区间的关系,这个关系可以用集合中的子区间到祖宗区间的距离来表示,因为有两种情况,所以当距离d[i] % 2 == 0时,当前区间的奇偶性和祖宗元素的奇偶性相同,d[i] % 2 ==1时,当前区间的奇偶性和祖宗元素的奇偶性不同
  • 每一个集合中的区间相对于祖宗区间的奇偶性是已知的,所以集合中所有区间的奇偶性关系都是已知的

代码实现

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int fa[N];	// 初始化每一个集合都是独立的
int d[N];	// 集合中子区间到祖宗区间的距离,可以判断奇偶关系
vector<int> ans;	// 离散化数组
int n, m;

struct node	// 存储每一个回答
{
	int x, y;
	string op;
}a[N];

int get(int u)	// 二分查找进行离散化
{
	int l = 0, r = ans.size();
	while(l + 1 != r)
	{
		int mid = l + r >> 1;
		if(ans[mid] < u) l = mid;
		else r = mid;
	}
	return r;
}

int find(int u)	// 找到当前区间的祖宗区间,并进行路径压缩和更新到祖宗区间的距离
{
	if(fa[u] != u)
	{
		int t = find(fa[u]);
		d[u] += d[fa[u]];
		fa[u] = t;
	}
	return fa[u];
}

int main()
{
	cin >> n >> m;
	for(int i = 1;i <= m; i++)
	{
		cin >> a[i].x >> a[i].y >> a[i].op;
		a[i].x--;
		ans.push_back(a[i].x);
		ans.push_back(a[i].y);
	}
	sort(ans.begin(), ans.end());
	ans.erase(unique(ans.begin(), ans.end()), ans.end());
	ans.insert(ans.begin(), 0);
	for(int i = 1; i <= m; i++) 
	{
		a[i].x = get(a[i].x);
		a[i].y = get(a[i].y);
	}
	for(int i = 1; i <= ans.size(); i++) fa[i] = i;
	for(int i = 1;i <= m; i++)
	{
		int fx = find(a[i].x), fy = find(a[i].y);
		string op = a[i].op;
		if(op == "even")
		{
			if(fx != fy)
			{
				d[fx] = d[a[i].x] ^ d[a[i].y];
				fa[fx] = fy;
			}
			else 
			{
				if((d[a[i].x] + d[a[i].y]) % 2 != 0)
				{
					cout << i - 1 << endl;
					return 0;
				}
			}
		}
		else
		{
			if(fx != fy)
			{
				d[fx] = d[a[i].x] ^ d[a[i].y] ^ 1;
				fa[fx] = fy;
			}
			else
			{
				if((d[a[i].x] + d[a[i].y]) % 2 == 0)
				{
					cout << i - 1 << endl;
					
					return 0;
				}
			}
		}
	}
	cout << m << endl;
	
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1228111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【0到1学习Unity脚本编程】第一人称视角的角色控制器

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;【0…

深信服AC流量管理技术

拓扑图 一.保证通道针对修仙部&#xff0c;访问网站&#xff0c;邮件&#xff0c;DNS&#xff0c;IM&#xff0c;办工 OA&#xff0c;微博论坛网上银行等常见应用保证带宽最低 50%&#xff0c;最高 100% 1. 先新建线路带宽 2.新增流量管理通道&#xff08;保证关键应用&#x…

吾爱破解置顶的“太极”,太好用了吧!

日常工作和娱乐&#xff0c;都需要用到不同类型的软件&#xff0c;哪怕软件体积不大&#xff0c;也必须安装&#xff0c;否则到用时找不到就非常麻烦了。 其实&#xff0c;很多软件不一定一样不剩地全部安装一遍&#xff0c;一方面原因是用的不多&#xff0c;另一方面多少有点…

安装第三方包报错 error: Microsoft Visual C++ 14.0 or greater is required——解决办法

1、问题描述 手动安装第三方软件时&#xff0c;可以使用setup.py&#xff0c;来安装已经下载的第三方包。一般文件下会存在setup&#xff0c;在所要安装库的目录下的cmd执行&#xff1a;python setup.py install报错&#xff1a;error: Microsoft Visual C 14.0 or greater i…

【LeetCode】二叉树OJ

目录 一、根据二叉树创建字符串 二、二叉树的层序遍历 三、二叉树的层序遍历 II 四、二叉树的最近公共祖先 五、二叉搜索树与双向链表 六、从前序与中序遍历序列构造二叉树 七、从中序与后序遍历序列构造二叉树 一、根据二叉树创建字符串 606. 根据二叉树创建字符串 - …

window上Clion配置C++版本的opencv

window上Clion配置opencv 注意版本一定要对的上&#xff0c;否则可能会出错&#xff0c;亲测 widnows 11mingw 8.1.0opencv 4.5.5 mingw8.1下载地址https://sourceforge.net/projects/mingw/ 配置环境变量 cmake下载 安装完添加环境变量 来到官网&#xff0c;下载 windows 对…

【华为HCIP | 华为数通工程师】刷题日记1116(一个字惨)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

真心建议看看这个盈亏平衡点计算方法及要点解析!

说实话&#xff0c;进行产品动态盈亏平衡计算是非常考验人的&#xff0c;因为不是人人都具备评估不同产品组合的盈利能力和掌握风险的方法。 当然最简单的方式就是套用诸如单产品动态盈亏平衡表之类的现成模板进行测算&#xff0c;可以实现以下三点基本需求&#xff1a; 弹性输…

AI实践与学习1_Milvus向量数据库实践与原理分析

前言 随着NLP预训练模型&#xff08;大模型&#xff09;以及多模态研究领域的发展&#xff0c;向量数据库被使用的越来越多。 在XOP亿级题库业务背景下&#xff0c;对于试题召回搜索单单靠着ES集群已经出现性能瓶颈&#xff0c;因此需要预研其他技术方案提高试题搜索召回率。…

OpenAI 董事会与 Sam Altman 讨论重返 CEO 岗位事宜

The Verge 援引多位知情人士消息称&#xff0c;OpenAI 董事会正在与 Sam Altman 讨论他重新担任首席执行官的可能性。 有一位知情人士表示&#xff0c;Altman 对于回归公司一事的态度暧昧&#xff0c;尤其是在他没有任何提前通知的情况下被解雇后。他希望对公司的治理模式进行重…

系列三、GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈

一、关系 GC算法&#xff08;引用计数法、复制算法、标记清除算法、标记整理算法&#xff09;是方法论&#xff0c;垃圾收集器是算法的落地实现。 二、4种主要垃圾收集器 4.1、串行垃圾收集器&#xff08;Serial&#xff09; 它为单线程环境设计&#xff0c;并且只使用一个线程…

【论文阅读】基于隐蔽带宽的汽车控制网络鲁棒认证(二)

文章目录 第三章 识别CAN中的隐藏带宽信道3.1 隐蔽带宽vs.隐藏带宽3.1.1 隐蔽通道3.1.2 隐藏带宽通道 3.2 通道属性3.3 CAN隐藏带宽信道3.3.1 CAN帧ID字段3.3.2 CAN帧数据字段3.3.3 帧错误检测领域3.3.4 时间通道3.3.5 混合通道 3.4 构建信道带宽公式3.5通道矩阵3.6 结论 第四章…

基于Vue+SpringBoot的大病保险管理系统 开源项目

项目编号&#xff1a; S 031 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S031&#xff0c;文末获取源码。} 项目编号&#xff1a;S031&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大…

腾讯云轻量数据库性能如何?轻量数据库租用配置价格表

腾讯云轻量数据库测评&#xff0c;轻量数据库100%兼容MySQL 5.7和8.0&#xff0c;腾讯云提供1C1G20GB、1C1G40GB、1C2G80GB、2C4G120GB、2C8G240GB五种规格轻量数据库&#xff0c;腾讯云百科txybk.com分享腾讯云轻量数据库测评、轻量数据库详细介绍、特性、配置价格和常见问题解…

六、文件上传漏洞

下面内容部分&#xff1a;参考 一、文件上传漏洞解释 解释&#xff1a;文件上传漏洞一般指的就是用户能够绕过服务器的规则设置将自己的木马程序放置于服务器实现远程shell&#xff08;例如使用蚁剑远程连接&#xff09;&#xff0c;常见的木马有一句话木马(php) 无需启用sho…

Gin框架源码解析

概要 目录 Gin路由详解 Gin框架路由之Radix Tree 一、路由树节点 二、请求方法树 三、路由注册以及匹配 中间件含义 Gin框架中的中间件 主要讲述Gin框架路由和中间件的详细解释。本文章将从Radix树&#xff08;基数树或者压缩前缀树&#xff09;、请求处理、路由方法树…

【Java SE】循环一些基本练习

判定一个数字是否是素数 public class Test {public static int is_sushu(int n) {if(n 1) {return 0;}int i ;for (i 2; i < Math.sqrt(n); i) {if(n % i 0 ) {break;}}if (i > n) {return 1;}return 0;}public static void main(String[] args) {Scanner scanner …

报错:HikariPool-1 - Exception during pool initialization.

问题发现&#xff1a; 原本可以运行的springboot2项目突然无法运行且报错&#xff0c;HikariPool-1 - Exception during pool initialization。 问题分析&#xff1a; 观察报错信息发现是JDBC连接失败&#xff0c;进而搜索HikariPool-1&#xff0c;搜索得知应该是applicatio…

Java中锁的深入理解

目录 对象头的理解 Monitor&#xff08;锁&#xff09; 锁类型 偏向锁 偏向锁的优化机制 轻量级锁 重量级锁 对象头的理解 在32位Java虚拟机中普通对象的对象头是占用8个字节&#xff0c;其中4个字节为Mark Word。用来存储对象的哈希值&#xff0c;对象创建后在JVM中的…