奇偶游戏 || 带权并查集
此题需要发现一个十分隐秘的限制。
首先以前缀和的角度考虑题面,若【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 }
目前的思维能力想不到真的太巧妙。。。

浙公网安备 33010602011771号