acwing 239. 奇偶游戏 并查集

地址  https://www.acwing.com/problem/content/241/

小A和小B在玩一个游戏。

首先,小A写了一个由0和1组成的序列S,长度为N。

然后,小B向小A提出了M个问题。

在每个问题中,小B指定两个数 l 和 r,小A回答 S[l~r] 中有奇数个1还是偶数个1。

机智的小B发现小A有可能在撒谎。

例如,小A曾经回答过 S[1~3] 中有奇数个1, S[4~6] 中有偶数个1,现在又回答 S[1~6] 中有偶数个1,显然这是自相矛盾的。

请你帮助小B检查这M个答案,并指出在至少多少个回答之后可以确定小A一定在撒谎。

即求出一个最小的k,使得01序列S满足第1~k个回答,但不满足第1~k+1个回答。

输入格式

第一行包含一个整数N,表示01序列长度。

第二行包含一个整数M,表示问题数量。

接下来M行,每行包含一组问答:两个整数l和r,以及回答“even”或“odd”,用以描述S[l~r] 中有奇数个1还是偶数个1。

输出格式

输出一个整数k,表示01序列满足第1~k个回答,但不满足第1~k+1个回答,如果01序列满足所有回答,则输出问题总数量。

数据范围
N≤10^9,M≤10000
输入样例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出样例:
3

解答1:

带权并查集

本题目有两个难点 

1 数据范围过大  有 10^9 

2 询问的内容如何转化到并查集

问题1 的解决办法是 使用离散化 将不同的数据映射到1 2 3 4。。。,由于只有10000次 询问,每次询问提供两个数据 所以只要提供10000*2的数据范围即可

问题2 的解决办法是 前缀和 如果提供 l和r的范围内1有奇数个还是偶数个 也就是计算 前缀和 sum[r] - sum[l-1]

另由于有偶数减偶数 奇数减奇数 都是偶数   只有两者不同类型分别是奇数偶数中的一种 才会出现最后的差是奇数

那么并查集其实也就是前缀和中每个元素的奇偶性

增加带权数组 所有的前缀和元素都会合并到一个祖先中,d[x]带权数组会记录x元素是否与根是同样的奇偶性

当得到新的询问时候 如果两个元素已经合并在同一个祖先下,那么就可以根据他们与祖先的异同得到他们的异同,再来判断他们与输入的异同是否一致 如果不一致就是发生矛盾,返回即可

代码如下

 1 #include <iostream>
 2 #include <string>
 3 #include <unordered_map>
 4 
 5 using namespace std;
 6 
 7 const int MAX_M = 20010;
 8 
 9 int N, M;
10 int n, m;
11 
12 int fa[MAX_M];
13 int d[MAX_M];
14 
15 int idx = 0;
16 
17 unordered_map<int, int> S;
18 
19 
20 //离散化
21 int get(int x) {
22     if (S.count(x) == 0) S[x] = ++idx;
23     return S[x];
24 }
25 
26 void init()
27 {
28     for (int i = 0; i < MAX_M; i++) {
29         fa[i] = i;
30     }
31 }
32 
33 int find(int x) {
34     if (fa[x] != x) {
35         int root = find(fa[x]);
36         d[x] += d[fa[x]]%2;
37         fa[x] = root;
38     }
39 
40     return fa[x];
41 }
42 
43 
44 int main()
45 {
46     
47     cin >> n >> m;
48     int res = m;
49     init();
50     for (int i = 1; i <= m; i++) {
51         int a, b; string s;
52         cin >> a >> b >> s;
53         a = get(a - 1); b = get(b);
54         int pa = find(a), pb = find(b);
55 
56         if (pa == pb) {
57             //查看两者是否符合之前的信息
58             if (s == "even")
59             {
60                 //两者奇偶性相同
61                 if( (d[a] + d[b]) % 2 != 0){
62                     //有矛盾
63                     res = i - 1;
64                     break;
65                 }
66             }
67             else if (s == "odd") {
68                 //两者奇偶性不同
69                 if ((d[a] + d[b]) % 2 != 1) {
70                     //有矛盾
71                     res = i - 1;
72                     break;
73                 }
74             }
75             else {
76                 cout << s << endl;
77                 cout << "error" << endl;
78                 break;
79             }
80         }
81         else {
82             //pa != pb
83             //合并
84             fa[pa] = pb;
85             int add = 0;
86             if (s == "odd") add = 1;
87             d[pa] = (d[a] + d[b] + add)%2;
88         }
89 
90 
91     }
92     cout << res << endl;
93 
94 
95 
96     return 0;
97 }
带权并查集

解法2 并查集扩展域

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <unordered_map>
 5 
 6 using namespace std;
 7 
 8 const int MAX_N = 20020*2; 
 9 const int Base = MAX_N / 2;
10 
11 unordered_map<int, int> S;
12 int n, m;
13 
14 int p[MAX_N];
15 
16 int idx = 0;
17 //离散化
18 int get(int x) {
19     if (S.count(x) == 0) S[x] = ++idx;
20     return S[x];
21 }
22 
23 int find(int x)
24 {
25     if (x != p[x]) p[x] = find(p[x]);
26 
27     return p[x];
28 }
29 
30 int main()
31 {
32     cin >> n >> m; int res = m;
33     for (int i = 0; i < MAX_N; i++) p[i] = i;
34 
35     for (int i = 1; i <= m; i++) {
36         int a, b; string s;
37         cin >> a >> b >> s;
38         a = get(a - 1); b = get(b);
39 
40         if (s == "even") {
41             //相同
42             if (find(a + Base) == find(b)) {
43                 res = i - 1;
44                 break;
45             }
46             p[find(a)] = find(b);
47             p[find(a + Base)] = p[find(b + Base)];
48         }
49         else {
50             if (find(a) == find(b)) {
51                 res = i - 1;
52                 break;
53             }
54             p[find(a+Base)] = find(b);
55             p[find(a )] = p[find(b + Base)];
56 
57         }
58         
59 
60     }
61     cout << res << endl;
62 
63     return 0;
64 }
扩展域
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
阿里打赏 微信打赏
原文地址:https://www.cnblogs.com/itdef/p/12115453.html