小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≤1e9,M≤10000
带权值并查集版本。
1 #include <iostream>
2 #include <cstring>
3 #include <cstdio>
4 #include <algorithm>
5 #include <unordered_map>
6
7 using namespace std;
8
9 const int N = 10010;
10 int p[N];
11 int d[N];
12 unordered_map<int,int> s;
13 int n, m;
14
15 int get(int x)
16 {
17 if (s[x] == 0) s[x] = ++ n;
18 return s[x];
19 }
20
21 int find(int x)
22 {
23 if (p[x] != x)
24 {
25 int u = find(p[x]);
26 d[x] ^= d[p[x]];
27 p[x] = u;
28 }
29 return p[x];
30 }
31
32 int main()
33 {
34 cin >> n >> m;
35 n = 0;
36 int res = m;
37
38 for (int i = 0; i < N; i ++ ) p[i] = i;
39
40 for (int i = 1; i <= m; i ++ )
41 {
42 int a, b; string op;
43 cin >> a >> b >> op;
44 a = get(a - 1), b = get(b);
45
46 int t = 0;
47 if (op == "odd") t = 1;
48
49 int fa = find(a), fb = find(b);
50 if (fa == fb)
51 {
52 if (d[a] ^ d[b] != t)
53 {
54 res = i - 1;
55 break;
56 }
57 }
58 else
59 {
60 p[fa] = fb;
61 d[fa] = d[a] ^ d[b] ^ t;//这里使用t来约束,使得d数组都是小于2的数
62 }
63 }
64
65 printf("%d", res);
66 return 0;
67 }
这里d数组是存储每个点之间的状态。
我们可以用s数组(即d数组)代表序列的前缀和,并且由于有t<2来约束s数组使得我们的s数组的值始终小于1,那么就可以使用异或的性质来判断,那么有:
s[l ~ r] 之间有偶数个1,等价于s[r]与s[l - 1]的奇偶性相同,即d[r] ^ d[l - 1] = 0。
s[l ~ r] 之间有奇数个1,等价于s[r]与s[l - 1]的奇偶性不同,即d[r] ^ d[l - 1] = 1。
一、当两个数奇偶性相同时
1.若两个元素a,b有公共的祖先,即两个元素在同一个集合中时,我们就判断一下d[a] ^ d[b]的值是否为0,若不为0则说明发生了冲突。
2.若两个元素a,b没有公共祖先,即两个元素没有在同一个集合中时,这时我们就不用关心两个元素异或的奇偶性,因为代表这两个元素之前没有被联系到一起,即使两个元素被使用过或者被覆盖掉也不用关心,因为这里没有给出序列的具体演示,那么就有无限种可能,总有一种可能符合。所以我们只需将两个元素联系到一起即可。
二、当两个元素奇偶性不同时
1.若两个元素a,b有公共的祖先,即两个元素在同一个集合中时,我们就判断一下d[a] ^ d[b]的值是否为1,若不为1则说明发生了冲突。
2.同上第一种情况即可。