zoukankan      html  css  js  c++  java
  • 239. 奇偶游戏

    题意:给你m个询问,每一个询问给出一个区间的左右端点和区间中的1的数量的奇偶性,输出不出现矛盾的最大的k值,即1k无矛盾,1k + 1矛盾。

    方法1:带权并查集 + 离散化

    设区间左右端点a和b,01序列的前缀和数组为s,那么[a, b]中的1的个数cnt = s[b] - s[a - 1], 那么cnt是奇是偶和s[b] - s[a - 1]有关:

    1. s[b]和s[a - 1]同奇偶,cnt为偶数
    2. s[b]和s[a - 1]不同奇偶,cnt为奇数
      那么对于输入的a b odd/even, 可以将他看成是s[b]和s[a - 1]的奇偶性,如果是a b odd则s[b]和s[a - 1]的奇偶性不同反之相同, 简单起见,把s[b]和s[a - 1]的奇偶关系之间看成是点b点a - 1的奇偶关系。

    这样就可以通过并查集来维护已经确定奇偶关系的点了。

    设点x,d[x]表示点x和它所在树的根之间的奇偶关系(同0,异1),所以两个点x和y的奇偶关系应该是,在x和y在同一棵树中时,它们和根的奇偶关系的异或,即:d[x] ^ d[y]

    当x和y不再同一棵树中时,需要根据题目给出的x和y的奇偶关系来合并x所在的树和y所在的树,合并x和y所在的树的关键问题就是得到x所在树的树根px和y所在树的树根py之间的奇偶关系,这样在令p[px] = py后,才能进一步得到x所在树的所有结点和py的奇偶关系。

    求x所在树的树根px和y所在树的树根py之间的奇偶关系:设输入的x和y的奇偶关系为ans(同奇,同偶为0,否则为1),可以知道在合并完x和y所在的树后,ans = d[x] ^ d[px] ^ d[y],所以可得:d[px] = d[x] ^ d[y] ^ ans,所以在合并x和y所在的树时,需要将d[px]赋值为d[x] ^ d[y] ^ ans,表示原本x所在树的根和y所在树的根之间的奇偶性关系。

    注意:合并x和y所在的树是在输入了x和y的奇偶关系,并且x和y不再同一棵树中才进行的,如果x和y已经在同一棵树中,那么就应该对这个query进行检验。

    #include<iostream>
    #include<vector>
    #include<algorithm>
    
    using namespace std;
    
    const int M = 20010;
    
    struct Node{
        int l, r;
        string s;
    };
    
    int p[M], d[M];
    vector<Node> query;
    int a[M], t;
    int n, m;
    int k;
    
    int find(int x){
        int l = 1, r = k;
        while(l < r){
            int mid = l + r >> 1;
            if(a[mid] >= x) r = mid;
            else l = mid + 1;
        }
        
        return l;
    }
    
    int get(int x){
        if(p[x] != x){
            int root = get(p[x]);
            d[x] = d[x] ^ d[p[x]];
            p[x] = root;
        }
        
        return p[x];
    }
    
    int merge(int x, int y, int e){
        int t = d[x] ^ d[y];
        x = get(x), y = get(y);
        p[x] = y, d[x] = t ^ e;
    }
    
    int main(){
        cin >> n >> m;
        
        for(int i = 1; i <= m; i ++){
            int l, r;
            string s;
            cin >> l >> r >> s;
            
            a[++ t] = l - 1; // 注意虽然输入的是 l r s, 但其实给出的是点r和点l - 1的奇偶关系
            a[++ t] = r;
            
            query.push_back({l - 1, r, s});
        }
        
        sort(a + 1, a + t + 1);
        
        for(int i = 1; i <= t; i ++)
            if(i == 1 || a[i] != a[k]) a[++ k] = a[i];
        
        
        for(int i = 1; i <= k; i ++) p[i] = i;
        
        int res = 0;
        
        for(int i = 0; i < query.size(); i ++){
            string s = query[i].s;
            int u = find(query[i].l), v = find(query[i].r);
            
            if(get(u) != get(v)) merge(u, v, s == "odd" ? 1 : 0);
            else if(s == "even" && d[u] ^ d[v]) break;
            else if(s == "odd" && d[u] ^ d[v] == 0) break;
            res ++;
        }
        
        cout << res;
        
        return 0;
    }
    

    方法2:扩展域并查集 + 离散化

    扩展域写法:对于一个点x,把x拆分成两个域,奇数域和偶数域。
    x的p域:是当x满足性质p的时候存在的域,这个域维护了和x满足性质p等价的其他信息。
    本题对于x扩展出它的奇数域和偶数域
    对于输入的x y odd, 检查如果x的偶数域和y的偶数域连通,或者x的奇数域和y的奇数域连通,说明出现矛盾。
    否则就将x的奇数域和y的偶数域连通,x的偶数域和y的奇数域连通。

    对于输入x y even, 检查如果x的奇数域和y的偶数域连通或者x的偶数域和y的奇数域连通,则出现矛盾。
    否则把x的偶数域和y的偶数域,x的奇数域和y的奇数域连通。

    注意:
    在判断x y even时:
    (x的奇数域和y的偶数域连通 || x的偶数和y的奇数域连通) <=> 出现矛盾
    而 x的偶数域和y的偶数域不连通并不能说明出现了矛盾
    因为(x的偶数域和y的偶数域不连通)不能决定(x的奇数域和y的偶数域连通)

    同理在判断x y odd时:(x的奇数域和y的奇数域连通 || x的偶数和y的偶数域连通) <=> 出现矛盾
    x的奇数域和y的偶数域不连通并不能说明出现了矛盾
    因为(x的奇数域和y的偶数域不连通)不能决定(x的奇数域和y的奇数域连通)

    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    const int M = 20010, N = 10010;
    
    int m, n;
    int p[M * 2];
    int a[M], ak, k;
    
    struct Node{
        int l, r;
        int x;
    }query[N];
    int qk;
    
    int get(int x){
        if(x != p[x]) p[x] = get(p[x]);
        return p[x];
    }
    
    int find(int x){
        int l = 1, r = k;
        
        while(l < r){
            int mid = l + r >> 1;
            if(a[mid] >= x) r = mid;
            else l = mid + 1;
        }
        
        return l;
    }
    
    int main(){
        cin >> n >> m;
        
        while(m --){
            int l, r;
            string str;
            
            cin >> l >> r >> str;
            
            a[++ ak] = l - 1, a[++ ak] = r;
            query[++ qk] = {l - 1, r, str == "odd" ? 1 : 0};
        }
        
        sort(a + 1, a + 1 + ak);
        
        for(int i = 1; i <= ak; i ++)
            if(i == 1 || a[i] != a[i - 1]) a[++ k] = a[i];
        
        for(int i = 1; i <= k * 2; i ++) p[i] = i;
        
        int res = 0;
        
        for(int i = 1; i <= qk; i ++){
            int x = find(query[i].l), y = find(query[i].r);
            // x表示x的偶数域,x + k表示x的奇数域
            if(query[i].x){
                if(get(x) == get(y)) break;
                p[get(x + k)] = get(y);
                p[get(x)] = get(y + k);
            }else{
                if(get(x) == get(y + k)) break;
                p[get(x + k)] = get(y + k);
                p[get(x)] = get(y);
            }
            
            res ++;
        }
        
        cout << res;
        
        return 0;
    }
    
  • 相关阅读:
    主机访问虚拟机网络服务失败
    关于接收者为指针的方法
    slice的部分说明
    ES基础知识
    静态语言和动态语言
    数据库设计三大范式
    SparkSQL小例子
    spark小例子
    spark 分析作者发布文章的总阅读量
    spark和hadoop差异
  • 原文地址:https://www.cnblogs.com/tomori/p/13896030.html
Copyright © 2011-2022 走看看