POJ-1733 Parity game

Parity game
题目链接
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 10580 Accepted: 4076
Description

Now and then you play the following game with your friend. Your friend writes down a sequence consisting of zeroes and ones. You choose a continuous subsequence (for example the subsequence from the third to the fifth digit inclusively) and ask him, whether this subsequence contains even or odd number of ones. Your friend answers your question and you can ask him about another subsequence and so on. Your task is to guess the entire sequence of numbers.

You suspect some of your friend’s answers may not be correct and you want to convict him of falsehood. Thus you have decided to write a program to help you in this matter. The program will receive a series of your questions together with the answers you have received from your friend. The aim of this program is to find the first answer which is provably wrong, i.e. that there exists a sequence satisfying answers to all the previous questions, but no such sequence satisfies this answer.
Input

The first line of input contains one number, which is the length of the sequence of zeroes and ones. This length is less or equal to 1000000000. In the second line, there is one positive integer which is the number of questions asked and answers to them. The number of questions and answers is less or equal to 5000. The remaining lines specify questions and answers. Each line contains one question and the answer to this question: two integers (the position of the first and last digit in the chosen subsequence) and one word which is either even' orodd’ (the answer, i.e. the parity of the number of ones in the chosen subsequence, where even' means an even number of ones andodd’ means an odd number).
Output

There is only one line in output containing one integer X. Number X says that there exists a sequence of zeroes and ones satisfying first X parity conditions, but there exists none satisfying X+1 conditions. If there exists a sequence of zeroes and ones satisfying all the given conditions, then number X should be the number of all the questions asked.
Sample Input

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
Sample Output

3
Source

CEOI 1999

题目大意

给出一个长度≤10^9的01串,进行≤5000次询问 L 到 R 区间里 1 的个数为奇数或偶数(L≤R≤串长),求出现矛盾是第几次之前询问。(如果始终没有矛盾输出最后一次)

题解
这题是不太明显的并查集(但是题目做多了会发现其实挺明显的)。
若能想到并查集,离散就不用说了。
我们假设我们已经知道哪些位置的0,哪些是1。我们对这个串做前缀和,那么每对描述L,R,“even”或“odd”就可以转化为 (s[R] - s[L-1])%2 = 1或0。
如果两者的差为奇数,那么两者奇偶性不同,否则相同。若 s[L-1] 和 s[R] 的奇偶性相同,那么我们就把 L-1 和 R 并起来,否则把 L-1 和 R+n 并起来。同样的道理,(L-1+n 和 R),(L-1+n 和 R+n) 分别在奇偶性不同和相同的情况下也要并起来,以防出错。
如果发现 i 和 j 连通(两个都不大于n,i < j),而且 s[j] - s[i] 是奇数那么一定出现了矛盾。相反还有一种情况就是 i 和 j+n 连通且 s[j] - s[i] 是偶数。

或者我们可以用一个 r 数组来表示每个 s[i] 的奇偶性。(只要能区分就好了,不一定1的就是奇数)。
由于一个两连通块合并时,两者对于奇偶和01的对应方式不一定一致(因为两者的差的奇偶只能判断两者奇偶性是否相同,不能明确哪个奇哪个偶),所以可能需要把某个连通块中所有的 r 反转(0和1交换),如果每次扫一趟又太慢了,所以我们想在刷并查集的时候就修正好它。如何修正呢?
我们首先规定,对于每棵树(每并查集连通块可以看成一棵树),它的根节点为0,其它的根据情况来。如果把根节 x 点连到其它树 T 上,那么下一次调 x 的子节点的时候,我们会把这个点的根节点修正为 x 的根节点也就是 T 的根,这一过程中,会经过之前的树根,而树根本来是0。如果此时我们把树根变成1,那么我们就知道,此时这个点应该取反了。(这么听着感觉很别扭,还不如直接看看代码)。

代码
第一段

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5005;
int n,m,tt,id[maxn<<1],fa[maxn<<2];
struct js{
    int x,y,p;
}a[maxn];
int read(){int x;scanf("%d",&x);return x;}//读整数
int reab(){char s[10];scanf("%s",s);return s[0]=='o';}//读字符串
int get(int x){return x==fa[x]? x:fa[x]=get(fa[x]);}//并查集
int fin(int x)//二分,求离散后的值
{
    int L=1,R=tt;
    while (L<=R)
    {
        int mid=(R-L>>1)+L;
        if (id[mid]==x) return mid;
        if (id[mid]<x) L=mid+1;
        else R=mid-1;
    }
    return 0;
}
int main()
{
    m=read();n=read();
    for (int i=1;i<=n;i++)
    {
        a[i].x=id[(i<<1)-1]=read();
        a[i].y=id[i<<1]=read()+1;
        a[i].p=reab();
    }
    sort(id+1,id+(n<<1)+1);id[0]=-1;m=n<<1;
    for (int i=0,j=1;i<=m;i=j)//离散
    {
        while (id[i]==id[j]) j++;
        id[++tt]=id[j];
    }
    for (int i=n+1<<1;i;i--) fa[i]=i;//并查集初始化
    for (int i=1;i<=n;i++)
    {
        int x=fin(a[i].x),y=fin(a[i].y);//把两个数弄成离散后的值
        if ((get(x)==get(y)&&a[i].p)||(get(x)==get(y+n)&&!a[i].p)) {printf("%d\n",i-1);return 0;}
        //判断是否矛盾
        if (a[i].p) fa[get(x)]=get(y+n),fa[get(x+n)]=get(y);
               else fa[get(x)]=get(y),fa[get(x+n)]=get(y+n);
    }
    printf("%d",n);
    return 0;
}

第二段

#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=15005;
map<int,int>h;
int n,m,tt,id[maxn<<1],r[maxn<<1],fa[maxn<<1],u;
int read(){int x;scanf("%d",&x);return x;}
int reab(){char s[10];scanf("%s",s);return s[0]=='o';}
int get(int x){if (x==fa[x]) return x;u=get(fa[x]);r[x]=r[fa[x]]+r[x]&1;return fa[x]=u;}
//并查集,同时更新 r 数组 
bool add(int x,int y,int p)
{
    int fx=get(x),fy=get(y);
    if (fx==fy) return (r[x]^p^r[y]);
    fa[fy]=fx;r[fy]=r[x]^r[y]^p;
    return 0;
}
int main()
{
    read();n=read();
    h.clear();
    for (int i=n+1<<1;i;i--) fa[i]=i;
    for (int i=1;i<=n;i++)
    {
        int x=read(),y=read()+1;
        x=(!h[x])? h[x]=++tt:h[x];
        y=(!h[y])? h[y]=++tt:h[y];
        if (add(x,y,reab())) {printf("%d\n",i-1);return 0;}
    }
    printf("%d",n);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/79700450