POJ 1733 Parity game(边带权的并查集)

题目链接:点击这里

在这里插入图片描述
在这里插入图片描述

题意:

输入 n n 表示有一个长度为 n n 0 , 1 0,1 字符串, m m 表示接下来有 m m 行输入,接下来的 m m 行输入中 x , y , e v e n x, y, even 表示第 x x 到第 y y 个字符中间 1 1 的个数为偶数个; x , y , o d d x, y, odd 表示第 x x 到第 y y 个字符中间 1 1 的个数为奇数个。若 m m 句话中第 k + 1 k+1 是第一次与前面的话矛盾,则输出 k k

思路分析:

如果我们用 s u m sum 数组表示序列 S S 的前缀和,那么在每个回答中:

  1. S [ l r ] S[l \sim r] 有偶数个 1 1 ,等价于 s u m [ l 1 ] sum[l-1] s u m [ r ] sum[r] 奇偶性相同。
  2. S [ l r ] S[l \sim r] 有奇数个 1 1 ,等价于 s u m [ l 1 ] sum[l- 1] s u m [ r ] sum[r] 奇偶性不同。

注意,这里没有求出 s u m sum 数组,我们只是把 s u m sum 看作变量。

这样就可以把题目所给出的每次询问转换成奇偶性相同与否的判断,即给定若干个变量和关系,判
定这些关系可满足性的问题。注意,本题的传递关系不止一种:

  1. x 1 x_1 x 2 x_2 奇偶性相同, x 2 x_2 x 3 x_3 奇偶性也相同,那么 x 1 x_1 x 3 x_3 奇偶性相同。
  2. x 1 x_1 x 2 x_2 奇偶性相同, x 2 x_2 x 3 x_3 奇偶性不同,那么 x 1 x_1 x 3 x_3 奇偶性不同。
  3. x 1 x_1 x 2 x_2 奇偶性不同, x 2 x_2 x 3 x_3 奇偶性也不同,那么 x 1 x_1 x 3 x_3 奇偶性相同。

基于上述分析,关系之间具有传递性,故可用并查集来做。另外,还需要将数据离散化,因为 N 1 0 9 N≤10^9 ,但是查询次数 M 10000 M≤10000 ,实际用到的数据也就 20000 20000 ,所以,将 N N 离散化成数组下标可表示的范围,方便并查集的操作。

边权 d [ x ] d[x] 0 0 ,表示 x x f a [ x ] fa[x] 奇偶性相同; d [ x ] d[x] 1 1 ,表示 x x f a [ x ] fa[x] 奇偶性不同。

在路径压缩时,对 x x 到树根路径上的所有边权做异或(xor)运算,即可得到 x x 与树根的奇偶性关系。

这里假设离散化后 l 1 l-1 r r 的值分别是 x x y y

对于题目中的第一种询问:

对于题目中的第二种询问:

代码中将上述两种询问写到一起了,如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>

using namespace std;
const int N = 20010;

int n, m;
int p[N], d[N];
map<int,int> mp;

int get(int x)          // 在线离散化
{
    if(mp.count(x) == 0)    mp[x] = ++n;
    return mp[x];
}

int find(int x)
{
    if(x != p[x])
    {
        int root = find(p[x]);
        d[x] ^= d[p[x]];
        p[x] = root;
    }
    return p[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    
    n = 0;      // 用于离散化
    
    for(int i = 1; i < N; ++i)  p[i] = i;   // 初始化
    
    int res = m;
    for(int i = 1; i <= m; ++i)
    {
        int a, b;
        char op[6];
        scanf("%d%d%s", &a, &b, op);
        
        if(res != m)    continue;
        
        a = get(a - 1), b = get(b);
        
        int t = 0;                      // 询问有偶数个1
        if(op[0] == 'o')    t = 1;      // 询问有奇数个1
        
        int pa = find(a), pb = find(b);
        
        if(pa == pb)
        {
            if((d[a] ^ d[b]) != t)
                res = i - 1;
        }
        else
        {
            p[pa] = pb;
            d[pa] = d[a] ^ d[b] ^ t;
        }
    }
    
    printf("%d\n", res);
    
    return 0;
}
发布了844 篇原创文章 · 获赞 135 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/105153184