【POJ 1733】Parity game

题目描述

有一个长度为 N ( N 10 9 ) 01 序列,共给你 M ( M 10000 ) 条信息,每条信息给出 [ l , r ] 1 的个数的奇偶性,但只有前 k 条信息是正确的,请输出 k 的值。

算法分析

将信息拆成前缀和的形式,即 s u m r s u m l 1 的奇偶性。

奇偶性有一个类比异或的性质:

相加的两个加数奇偶性相同时,运算结果为偶数;相加的两个加数奇偶性不同时,运算结果为奇数。

对应异或的性质是:

两个位相同时,异或的结果为 0 ;两个位不同时,异或的结果为 1

则上述式子等价于:

s u m r s u m l 1 = 0 s u m l 1 s u m r 的奇偶性相同;若 s u m r s u m l 1 = 1 s u m l 1 s u m r 的奇偶性不同。

这个我们用带权并查集维护,具体实现为:

  • s u m l 1 s u m r 在同一集合中时,判断两者到根节点的路径的异或值是否与给出的信息不同,如果不同则发现矛盾。
  • s u m l 1 s u m r 不在同一集合中时,将其中一个节点的根节点合并到另一棵树中,权值为两个节点到各自根结点的距离与信息给出奇偶性的异或和。

当然,由于 N 的取值范围较大,我们需要先进行离散化。

代码实现

#include <cstdio>
#include <algorithm>
const int maxm=5005;
struct opt {
    int l,r,t;
} a[maxm];
int hash[2*maxm],idx=0;
inline int query(int x) {return std::lower_bound(hash,hash+idx,x)-hash;}
int fa[2*maxm],d[2*maxm];
int find(int x) {
    if(x==fa[x]) return x;
    int rt=find(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=rt;
}
int main() {
    int n,m;scanf("%d%d",&n,&m);
    char s[10];
    for(int i=0;i<m;++i) {
        scanf("%d%d%s",&a[i].l,&a[i].r,s);a[i].t=(s[0]=='o');
        hash[idx++]=a[i].l-1;hash[idx++]=a[i].r;
    }
    std::sort(hash,hash+idx);idx=std::unique(hash,hash+idx)-hash;
    for(int i=0;i<idx;++i) {
        fa[i]=i;d[i]=0;
    }
    for(int i=0;i<m;++i) {
        int l=query(a[i].l-1),r=query(a[i].r);
        int x=find(l),y=find(r);
        if(x==y) {
            if((d[l]^d[r])!=a[i].t) {
                printf("%d\n",i);
                return 0;
            }
        }
        else {
            d[x]=a[i].t^d[l]^d[r];
            fa[x]=y;
        }
    }
    printf("%d\n",m);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/WHZ2018/article/details/81260769