P5937 [CEOI1999]Parity Game

洛谷P5937 P5937 [CEOI1999]Parity Game


前言:

个人感觉这道题初看想不到并查集啊!(说实话我题都没读懂,第二遍才读懂)

好了,转入正题,这道题我们可以用两种方法A掉:种类并查集&带权并查集


题目简述:

给定01序列的长度n,询问和答案的个数m

对于每行的询问和回答先给出两个整数,表示询问区间(闭区间)

再给出一个字符串回答:“odd”表示区间有奇数个1,“even”表示区间有偶数个1

如果存在一个01序列满足第1个到第x个的回答,不满足第x+1个回答,则输出x(如果m个回答都满足,那肯定就输出m)

数据范围:

对于100%的数据,1≤N≤10^9,1≤M≤5×10^3

因为N有点大,所以我们需要使用到离散化! 来将大数据映射到小范围的小数据


引入:

其实本题和程序自动分析这道题很像:都是给定若干个变量和关系,判断这些关系的可满足性问题

不过我们这道题的传递关系不止一种(涉及到了奇偶性):

  1. 若x1与x2的奇偶性相同,x2与x3的奇偶性相同,那么x1与x3的奇偶性相同

  2. 若x1与x2的奇偶性相同,x2与x3的奇偶性不同,那么x1与x3的奇偶性不同

  3. 若x1与x2的奇偶性不同,x2与x3的奇偶性不同,那么x1与x3的奇偶性不同


种类并查集:

(因为我认为这道题种类并查集比较好想也比较好理解,所以先讲种类并查集的做法ovo,下面通过问答的形式给出思路)

  • 为什么想到种类并查集?
  1. 本题需要记录与自己相同的和不同的,那么我们可以将相同的理解为“朋友”,将不同的理解为“敌人”

  2. 而题目中的奇偶性又具有传递性(见上“引入部分”),所以我们还需要维护这种传递性——于是我们想到了种类并查集

  • 怎么维护传递性&怎么判断回答是否正确?
  1. 每遇到一个回答,我们先判断当前两个区间端点的奇偶性:

①对于“even”回答,若左端点在右端点“敌人”的集合中或右端点在左端点“敌人”的集合中,则回答错误

②对于“odd”回答,若左端点在右端点“朋友”的集合中或右端点在左端点“朋友”的集合中,则回答错误

  1. 如果回答是正确的,那我们就进行合并操作:

①对于“even”,我们将左端点和右端点的“朋友”、“敌人”集合分别合并

②对于“odd”,我们将左端点的“朋友”与右端点的“敌人”合并,将左端点的“敌人”与右端点的“朋友”合并

  • 怎么离散化?

可见浅谈离散化这篇博客qvq

  • 下面给出种类并查集做法的完整代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,tot,res,b[100010],fa[100010]; struct node {
int u,v;
string op;
} a[100010]; inline int find(int x) {
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
} int main() {
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;i++) {
scanf("%d%d",&a[i].u,&a[i].v);
cin>>a[i].op;
b[++tot]=a[i].u;
b[++tot]=a[i].v;
}
sort(b+1,b+1+tot);
res=unique(b+1,b+1+tot)-(b+1);
for(register int i=1;i<=m;i++) { //STL实现离散化的三部曲
a[i].u=lower_bound(b+1,b+1+res,a[i].u-1)-b;
a[i].v=lower_bound(b+1,b+1+res,a[i].v)-b;
}
for(register int i=1;i<=2*res;i++) fa[i]=i;
for(register int i=1;i<=m;i++) {
if(a[i].op=="even") { //如果回答是偶数个
if(find(a[i].u)==find(a[i].v+res)||find(a[i].u+res)==find(a[i].v)) { //判断奇偶性
printf("%d",i-1); //注意是i-1而不是i
return 0;
}
fa[find(a[i].u)]=find(a[i].v);
fa[find(a[i].u+res)]=find(a[i].v+res);
}
else {
if(find(a[i].u)==find(a[i].v)||find(a[i].u+res)==find(a[i].v+res)) {
printf("%d",i-1);
return 0;
}
fa[find(a[i].u)]=find(a[i].v+res);
fa[find(a[i].u+res)]=find(a[i].v);
}
}
printf("%d",m);
return 0;
}

带权并查集:

  • 怎么想到带权并查集?

我们不妨将区间转换为一棵树,然后用0/1来表示边权,如下草图:

我们可以发现,任意区间都可在在树上表示并求得区间的奇偶性(0/1连成字符串就好)

  • 怎么转换为维护带权并查集?
  1. 我们将输入中的回答转换为0或1存储:“even”标记为0,“odd”标记为1

  2. 我们用dis[x]来记录x与fa[x]的奇偶性关系:为0就相同,反之不同

  3. 在路径压缩时,对x到根节点路径上的所有边权做异或(xor)运算,就可以得到x与root的奇偶性关系(都是0/1,所以想到异或)

如上图:dis[x]=1 xor 0 xor 1=0,dis[y]=0 xor 1 xor 1=0

  1. 对于每个问题,先检查左右端点是否在同一个集合内(奇偶性是否已知):

①在的话就计算dis[l] xor dis[r](即两端点的奇偶性关系),若与回答相矛盾(dis[l] xor dis[r]≠ans),那么回答错误

②不在的话就进行合并操作。此时设两个集合的树根为xl、xr,令xl作为xr的孩子,那就要求出dis[xl]=dis[l] xor dis[r] xor ans

  • 下面给出带权并查集做法的完整代码:
#include <bits/stdc++.h>
using namespace std;
string s;
int n,m,tot,res;
int b[100010],fa[100010],dis[100010]; struct node {
int u,v,op;
} a[100010]; inline int find(int x) {
if(fa[x]==x) return x;
int root=find(fa[x]);
dis[x]^=dis[fa[x]]; //路径压缩求出x与树根的奇偶性关系
return fa[x]=root;
} int main() {
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;i++) {
scanf("%d%d",&a[i].u,&a[i].v);
cin>>s;
if(s=="even") a[i].op=0; //转换回答
else a[i].op=1;
b[++tot]=a[i].u;
b[++tot]=a[i].v;
}
sort(b+1,b+1+tot);
res=unique(b+1,b+1+tot)-(b+1);
for(register int i=1;i<=2*res;i++) fa[i]=i;
for(register int i=1;i<=m;i++) {
int x=lower_bound(b+1,b+1+res,a[i].u-1)-b; //直接判断一个取一个,就不用单独处理
int y=lower_bound(b+1,b+1+res,a[i].v)-b;
int xx=find(x),yy=find(y);
if(xx==yy) { //在一个集合中
if((dis[x]^dis[y])!=a[i].op) { //判断回答是否正确
printf("%d",i-1);
return 0;
}
}
else {
fa[xx]=yy;
dis[xx]=dis[x]^dis[y]^a[i].op; //求左端点树根成为右端点树根孩子后的边权
}
}
printf("%d",m);
return 0;
}

然后,感谢一下我的同桌给我的指导

最后,有什么问题欢迎各位dalao们指出来qwq


[CEOI1999]Parity Game 题解的更多相关文章

  1. 题解 P5937 【[CEOI1999]Parity Game】

    这道题有两种做法,一种是 扩展域(种类并查集),一种是 边带权(带权并查集).种类并查集貌似应该都比带权并查集简单,所以先讲种类并查集的做法,再讲带权并查集 种类并查集 若 sum[ l ~ r ] ...

  2. 算法与数据结构基础 - 排序(Sort)

    排序基础 排序方法分两大类,一类是比较排序,快速排序(Quick Sort).归并排序(Merge Sort).插入排序(Insertion Sort).选择排序(Selection Sort).希尔 ...

  3. 算法与数据结构基础 - 数组(Array)

    数组基础 数组是最基础的数据结构,特点是O(1)时间读取任意下标元素,经常应用于排序(Sort).双指针(Two Pointers).二分查找(Binary Search).动态规划(DP)等算法.顺 ...

  4. CF1272E. Nearest Opposite Parity 题解 广度优先搜索

    题目链接:http://codeforces.com/contest/1272/problem/E 题目大意: 有一个长度为n的数组 \(a\) ,数组坐标从 \(1\) 到 \(n\) . 假设你现 ...

  5. CF1110A Parity 题解

    Content 求下面式子的奇偶性,其中 \(a_i,k,b\) 会在输入中给定. \[\sum\limits_{i=1}^k a_i\cdot b^{k-i} \] 数据范围:\(2\leqslan ...

  6. Codeforces 549C. The Game Of Parity[博弈论]

    C. The Game Of Parity time limit per test 1 second memory limit per test 256 megabytes input standar ...

  7. Codeforces Round #180 (Div. 2) C. Parity Game 数学

    C. Parity Game 题目连接: http://www.codeforces.com/contest/298/problem/C Description You are fishing wit ...

  8. poj1733 Parity Game(扩展域并查集)

    描述 Now and then you play the following game with your friend. Your friend writes down a sequence con ...

  9. POJ1733 Parity game 【扩展域并查集】*

    POJ1733 Parity game Description Now and then you play the following game with your friend. Your frie ...

随机推荐

  1. Java实现 第十一届 蓝桥杯 (本科组)省内模拟赛

    有错误的或者有问题的欢迎评论 计算机存储中有多少字节 合法括号序列 无向连通图最少包含多少条边 字母重新排列 凯撒密码加密 反倍数 正整数的摆动序列 螺旋矩阵 小明植树 户户通电 计算机存储中有多少字 ...

  2. Java实现 LeetCode 698 划分为k个相等的子集(递归)

    698. 划分为k个相等的子集 给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等. 示例 1: 输入: nums = [4, 3, 2, 3, ...

  3. Java实现 LeetCode 300 最长上升子序列

    300. 最长上升子序列 给定一个无序的整数数组,找到其中最长上升子序列的长度. 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,10 ...

  4. Java实现 LeetCode 232 用栈实现队列

    232. 用栈实现队列 使用栈实现队列的下列操作: push(x) – 将一个元素放入队列的尾部. pop() – 从队列首部移除元素. peek() – 返回队列首部的元素. empty() – 返 ...

  5. Java实现 蓝桥杯VIP 算法提高 班级排名

    算法提高 班级排名 时间限制:1.0s 内存限制:256.0MB 问题描述 达达在陶陶的影响下,也对学习慢慢的产生了兴趣. 他在每次考试之后,都会追着老师问,自己在班级的总名次是多少.考试一多,老师也 ...

  6. Java实现 洛谷 P1115 最大子段和

    import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scann ...

  7. 从linux源码看socket的阻塞和非阻塞

    从linux源码看socket的阻塞和非阻塞 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 大部分高性能网络框架采用的是非阻塞模式.笔者这次就从linux ...

  8. 批量执行app自动化测试思路设计图

  9. Vue使用js鼠标蜘蛛特效

    1. 在src下新建文件夹utils,里面新建文件canvas-nest.js,将代码复制进去.(可以自己定义存放路径) !function() { function n(n, e, t) { ret ...

  10. Java对象转换Json的细节处理

    一.fastJson 1.fastJson在转换java对象为json的时候,默认是不序列化null值对应的key的 也就是说当对象里面的属性为空的时候,在转换成json时,不序列化那些为null值的 ...