Parity game (POJ - 1733,边带权 || 拓展域 并查集)

本文探讨了在大规模01串上进行区间奇偶性查询的优化算法,通过离散化和并查集,提出了两种解决方案:边带权法和拓展域法,以高效判断回答的矛盾性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.题目链接:

POJ-1733

二.题目大意:

小 A 有一个 01 串,小 B 对小A进行 m 次询问,每次询问给出一个区间,小 B 则给出此区间 1 的奇偶性.

小 B 怀疑 小 A 有时会说谎(自我矛盾)

输出至少多少个回答后可以判定 小 A  在说谎.

三.分析:

如果用 sum[i] 表示 01 串的 1 的个数的前缀和.

那么在第 i 次询问中

如果 sum[l ~ r] 有偶数个 1,等价于 sum[l - 1] 与 sum[r] 的奇偶性相同.

如果 sum[l ~ r] 有奇数个 1,等价于 sum[l - 1] 与 sum[r] 的奇偶性不同.

关系的传递为:

1. 若 a 与 b 的奇偶性相同,b 与 c 的奇偶性相同,则 a 与 c 的奇偶性相同.

2. 若 a 与 b 的奇偶性相同,b 与 c 的奇偶性不同,则 a 与 c 的奇偶性不同.

3 若 a 与 b 的奇偶性不同,b 与 c 的奇偶性不同,则 a 与 c 的奇偶性相同.

另外由于 n 的数据范围过大,这里可以用离散化将区间范围缩小到 [1 ~ 2m].

为了处理上述的关系传递关系,下面有两种解法.

① 边带权:

用 d[x] 表示 x 与 fa[x] 的奇偶性,d[x] 为 0,表示 x 与 fa[x] 的奇偶性相同,反之不同.

这样在路径压缩的过程中,对 x 到 根节点上所有的边异或一遍,则可得到 d[x] 与 根节点的奇偶关系.

对于每次询问,设在离散化之后 l - 1 与 r 的值分别为 u 和 v,设 ans 为小 A 的回答.

先检查 x 与 y 的祖先是否相同.

若相同,则去验证小 A 回答的正确性.

即检查 d[u]^d[v] 是否等于 ans.

若不相同,则将 u 的祖先挂在 v 的祖先上.

现来考虑 d[fau] 的变化.

u 到 v 的路径为 u->fau, fau->fav, fav->y.

由小 A 的回答可得

ans == d[x]^d[fau]^d[y].

即 d[fau] = d[x]^d[y]^ans.

② 拓展域

将每个点分为奇数域和偶数域,即将 u 分为两个点来表示 u_odd, v_even.

若 ans == 0,则合并 u_odd 与 v_odd,u_even 与 v_even.

若 ans == 0,则合并 u_odd 与 v_even,u_odd 与 v_even.

立即推:

若 u_odd 与 v_odd 在同一集合中,则说明 u 与 v 奇偶性相同.

若 u_odd 与 v_even 在同一集合中,则说明 u 与 v 奇偶性不同.

由此可以用来检验此 ans 的真伪.

四.代码实现:

① 边带权

#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-6
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
using namespace std;

const int M = (int)2e4;
const int mod = (int)1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

char str[5];
int d[M + 5];
int fa[M + 5];
int a[M + 5], len;
struct node
{
    int l, r, ans;
}query[M + 5];

void discrete()
{
    sort(a + 1, a + len + 1);
    len = unique(a + 1, a + len + 1) - (a + 1);
}

int find_id(int x)
{
    return lower_bound(a + 1, a + len + 1, x) - a;
}

int find_fa(int x)
{
    if(x == fa[x])  return x;
    int root = find_fa(fa[x]);
    d[x] ^= d[fa[x]];
    return fa[x] = root;
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d %d %s", &query[i].l, &query[i].r, str);
        query[i].ans = (str[0] == 'o');
        a[++len] = query[i].l - 1;
        a[++len] = query[i].r;
    }
    discrete();
    for(int i = 1; i <= len; ++i)
        fa[i] = i;
    int u, v, fau, fav;
    for(int i = 1; i <= m; ++i)
    {
        u = find_id(query[i].l - 1);
        v = find_id(query[i].r);
        fau = find_fa(u);
        fav = find_fa(v);
        if(fau == fav)
        {
            if((d[u]^d[v]) != query[i].ans)
            {
                printf("%d\n", i - 1);
                return 0;
            }
        }
        else
        {
            fa[fau] = fav;
            d[fau] = d[u]^d[v]^query[i].ans;
        }
    }
    printf("%d\n", m);
    return 0;
}

② 拓展域:

#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-6
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
using namespace std;

const int M = (int)2e4;
const int mod = (int)1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

char str[5];
int a[M + 5], len;
int fa[M * 2 + 5];

struct node
{
    int l, r, ans;
}query[M + 5];

void discrete()
{
    sort(a + 1, a + len + 1);
    len = unique(a + 1, a + len + 1) - (a + 1);
}

int find_id(int x)
{
    return lower_bound(a + 1, a + len + 1, x) - a;
}

int find_fa(int x)
{
    if(x == fa[x])  return x;
    return fa[x] = find_fa(fa[x]);
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d %d %s", &query[i].l, &query[i].r, str);
        query[i].ans = (str[0] == 'o');
        a[++len] = query[i].l - 1;
        a[++len] = query[i].r;
    }
    discrete();
    for(int i = 1; i <= len * 2; ++i)
        fa[i] = i;
    int u, v, u_odd, v_odd, u_even, v_even;
    for(int i = 1; i <= m; ++i)
    {
        u = find_id(query[i].l - 1);
        v = find_id(query[i].r);
        u_odd = u, u_even = u + len;
        v_odd = v, v_even = v + len;
        if(query[i].ans == 0)
        {
            if((find_fa(u_odd)) == find_fa(v_even))
            {
                printf("%d\n", i - 1);
                return 0;
            }
            fa[find_fa(u_odd)] = find_fa(v_odd);
            fa[find_fa(u_even)] = find_fa(v_even);
        }
        else
        {
            if(find_fa(u_odd) == find_fa(v_odd))
            {
                printf("%d\n", i - 1);
                return 0;
            }
            fa[find_fa(u_odd)] = find_fa(v_even);
            fa[find_fa(u_even)] = find_fa(v_odd);
        }
    }
    printf("%d\n", m);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值