POJ 1733 Parity game(种类并查集+哈希)

Parity game

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' or `odd' (the answer, i.e. the parity of the number of ones in the chosen subsequence, where `even' means an even number of ones and `odd' 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
 
题意大概是说给定一个N,表示接下来该二进制数最多有N位数(N〈=10^9),然后在给一个数M(M<=5000),表示接下来有M个询问,如果接下来的询问与上面的询问矛盾,就输出之前不矛盾的回合数。而每次询问给定2个数x和y,然后后面一个字符串even或者odd,表示从第x位到第y位有偶数个1或者奇数个1(eg:1 2 even   表示第1位到第2位数之间有偶数个1)
 
做法:(先不考虑哈希)个人感觉就是个种类并查集。首先要把题意转换一下,从第x位到第y位有偶数个1就可以看成x-1和y的种类相同{:::x-1和y种类相同表示从第1位到第x-1位的1的个数和从第1位到第y位的1的个数的奇偶性相同,这里可以举个例子:比如x=3,y=6, even 表示从3到6有偶数个1,假设第一位到第二位有奇数个1,那么从第1位到第6位也有奇数个1,所以从第1位到第2位的1个数目的奇偶性和从第1位到第6位1的数目的奇偶性相同:::},同理可得如果x到Y有奇数个1的话表示x-1和y的种类不同,至此,上述题意已经转换为种类并查集。对于每位数有2个种类,当然种类是相对的。

下面是种类并查集的做法(个人总结):对于每个不同的集合首先要确立一个总的根节点,来进行参照对比,(值得注意的是:这个根节点的值必须为0),然后每次往这个集合(定位集合1吧)加点的时候先判断加的那个点(定为点2)(点2所在集合设为集合2)的种类是否和以集合1为参照的种类相同(比如说集合1内有一个点1,如果点1和点2的种类相同,但是他们在各自集合表示的种类不同,比如点1在集合1中位物种1,点2在集合2中为物种2,既v[1]=0,v[2]=1,v表示种类,0表示物种1,1表示物种2,此时集合1和点2所在集合的参照就不同),如果两者的参照不同,那么就把其中一个集合(比如集合2)的根值改变成与另一个集合(集合1)参照相同,然后通过Find函数,把该集合(集合2)所有点的值都改成与集合1参照相同的对应值,并把集合2的根值变成集合1的根值的儿子。如果两个集合的参照本来就相同,就无需改变集合的值,只需要把集合2的根值变成集合1的根值的儿子就可以了。(这里还需要注意的是:必须要通过路径压缩把所有的点变成一个根值的儿子,这是为了防止多次变种,这也是为什么之前根节点必须为0的原因)

	比如本题的话:
			1、先判断2个点在不在同一个集合,如果在一个集合判断2点的相对奇偶性(也就是种类) 			是否与题目所给的相同。
			2.如果不在一个集合,用Find函数找出点x-1,y的根值为tx,ty;
			       tx......>ty
				|	|
				|	|
		                |	|		
				x-1	y
			如果x-1,y的参照种类不同,改变tx的值,然后把tx变成ty的儿子
			如果x-1,y的参照种类相同,直接把tx变成ty的儿子
			{注意路径压缩}

看到这是不是以为题目就这样了?但是你有没有注意到位数N最大是10^9,也就是说你不能开一个这么大数组来保存该点的父亲和种类,这个问题怎么解决呢?这里注意到询问只有最多只有5000次,也就是说出现的点最多有10000个,那么我们只需要用把这10000个数(不管其大小,只管它出现的先后顺序)分别映射到从0-9999内。这种算法就是哈希,具体实现方法还是见代码吧。
#include<stdio.h>
#include<string.h>
const int MAX = 10007;
int f[MAX],v[MAX];
int head[MAX],nxt[MAX],pnt[MAX];
int E=0;
int hash(int x) {                         //哈希映射和查找
    int h=x%MAX;
    for(int e=head[h];e!=-1;e=nxt[e])
    {
        if(pnt[e]==x) return e;
    }
    pnt[E]=x;
    nxt[E]=head[h];
    head[h]=E++;
    return E-1;
}
int Find(int x)
{
    if(f[x]!=x){
        int root=Find(f[x]);
        v[x]=(v[x]+v[f[x]])%2;          //更新集合内所有的点(集合1+集合2)
        return f[x]=root;
    }
    return x;
}
int main()
{
    int n,m;
    while(scanf("%d",&n)!=EOF)
    {
        E=0;
        memset(head,-1,sizeof(head));
        memset(nxt,0,sizeof(nxt));
        memset(pnt,0,sizeof(pnt));
        scanf("%d",&m);
        int cnt=-1,flag=1;
        for(int i=0;i<=2*m;i++)
        {
            f[i]=i;
            v[i]=0;
        }
        for(int i=0;i<m;i++)
        {
            int x,y,oe;
            char s[10];
            scanf("%d %d %s",&x,&y,s);
            x--;
            x=hash(x),y=hash(y);
            if(flag)
            {
                if(strcmp(s,"even")==0) oe=0;
                else oe=1;
                int tx=Find(x);
                int ty=Find(y);
                if(tx==ty)
                {
                    if((v[x]+v[y])%2!=oe) {flag=0;cnt=i;}       //判断在集合内2点的相对奇偶性与题目的是<span style="white-space:pre">								</span>  否吻合
                }
                else{
                    f[tx]=ty;                           //种类并查集 更新集合2 的根节点
                    v[tx]=(v[x]+v[y]+oe)%2;
                }
            }
        }
        if(cnt==-1) cnt=m;
        printf("%d\n",cnt);
    }
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值