zoukankan      html  css  js  c++  java
  • 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驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    linux常用命令
    mysql 开发基础系列20 事务控制和锁定语句(上)
    sql server 性能调优之 资源等待 CXPACKET
    mysql 开发基础系列19 触发器
    mysql 开发基础系列18 存储过程和函数(下)
    mysql 开发基础系列17 存储过程和函数(上)
    sql server 性能调优之 资源等待PAGEIOLATCH
    mysql 开发基础系列16 视图
    mysql 开发基础系列15 索引的设计和使用
    sql server 性能调优之 当前用户请求分析 (1)
  • 原文地址:https://www.cnblogs.com/itdef/p/12115453.html
Copyright © 2011-2022 走看看