Loading

奇偶游戏 || 带权并查集

 

此题需要发现一个十分隐秘的限制。

首先以前缀和的角度考虑题面,若【r,l】中存在奇数个1,就说明s[r] - s[l - 1] 是奇数,就说明s[r]和s[l - 1]是不同奇偶性的,此时将只是将s[r]和s[l - 1]看作两个不同的变量(十分的抽象),那么这个性质是具有传递性和对称性的(x和y的奇偶性相同则y和x的奇偶性相同。x和y的奇偶性相同,y和z的奇偶性相同,则x和z的奇偶性相同)所以可以用并查集维护。那么首先这个题的N十分大M十分小,很明显在提示用离散化搞一下(在线离散化做法),然后用一个带权并查集维护这个性质,那么因为性质有两个,所以可以用0和1代表(类似的,n个性质可以用 % n代表),首先先解释一下带权并查集的维护 :

1 int p[N], w[N];
2 int find(int x) {
3     if (p[x] == x) return x; 
4     int t = p[x]; //现将现在的父亲节点记下来
5     p[x] = find(p[x]); //让父亲节点归属于祖宗节点
6 /*此时的x归属于祖宗节点(x就是祖宗节点),这个点的权值就分成了两部分,一部分是父亲到祖宗节点的权值和自己到父亲的权值*/
7     w[x] += w[t];
8     return p[x]; 
9 }   

然后在线处理一下区间,首先在线离散化一下两个端点(注意是l - 1和r),然后检查是否在一个集合中。

1.如果在一个集合中说明这两个点之前有过关系,那么这两个点之间一定存在一个中间节点(也就是跟节点),已知用w[x]代表x与根节点的相对关系,w[y]代表y与根节点的相对关系,那么我们就可以求出x和y之间的绝对关系(举例来说,x和根节点的关系如果是0的话就说明x和根节点的奇偶性不同,如果y和根节点的关系是1的话就说明y与根节点的奇偶性相同,那么x与y的奇偶性自然也就不同),所以x和y的关系可以用w[x] ^ w[y] 表示,那么如果这个值和给的值不同就说明有冲突结束程序。

2.如果不在一个集合中,那么就需要合并一下,这时候列出等式w[p[x]] ^ w[x] ^ w[y] = t,自然可以推出w[p[x]] = w[x] ^ w[y] ^ t。

代码 :

 1 #include <iostream>
 2 #include <unordered_map>
 3 using namespace std;
 4 
 5 const int N = 10010;
 6 unordered_map<int, int> hs;
 7 int p[N], w[N], idx;
 8 
 9 int find(int x) {
10     if (p[x] == x) return x;
11     int t = p[x];
12     p[x] = find(p[x]);
13     w[x] ^= w[t];
14     return p[x];
15 }
16 int get(int x) {
17     if (!hs.count(x))
18         hs[x] = ++ idx;
19     return hs[x];
20 }
21 int main() {
22     int n, m;
23     cin >> n >> m;
24     for (int i = 1;i <= N;i ++) 
25         p[i] = i;
26     for (int i = 0;i < m;i ++) {
27         int x, y;
28         string ty;
29         cin >> x >> y >> ty;
30         int t = 0;
31         if (ty == "odd") 
32             t = 1;
33         int a = get(x - 1), b = get(y);
34         int pa = find(a), pb = find(b);
35         if (pa == pb) {
36             if (w[a] ^ w[b] != t) {
37                 cout << i << '\n';
38                 return 0;
39             } 
40         } else {
41             p[pa] = pb;
42             w[pa] = w[a] ^ w[b] ^ t;
43         }
44     }
45     cout << m << '\n';
46     return 0;
47 }
View Code

 

目前的思维能力想不到真的太巧妙。。。

posted @ 2023-02-11 23:55  KakaDBL  阅读(41)  评论(0)    收藏  举报