【并查集】Parity


Ural 1003

Parity 奇偶
Time Limit: 2.0 second
Memory Limit: 16MB


Background

偶尔你跟朋友玩以下的游戏:你的朋友写下一串包含1和0的数串让你猜,你可以从中选择一个连续的子串(例如其中的第3到第5个数字)问他,该子串中包含了奇数个还是偶数个1,他会回答你的问题,然后你可以问其他子串,等等。

Problem

你的任务是猜出完整的子串。你怀疑你的朋友的答案可能有错,你想质疑他的错误。 所以要写这个程序帮忙验证。程序接受一系列你的问题以及你朋友给出的答案。程序的目标是找出错误的第一个答案,也就是某一子串满足先前的所有答案,而不满足当前这个答案。

Input

输入包含多组测试数据。 首行为整个数串的长度,小于或等于1000000000。第二行为一个正整数,表示问题及答案数,小于或等于5000。之后每行表示问题及答案。格式:2个整数(即子串的第一个及最后一个数位),答案为一个单词:'even'(偶),或'odd'(奇),即选择的子串中1的个数是奇数个,则答案为odd,否则为even。最后以-1结束。

Output

输出x,x表示存在01数串满足前x个奇偶条件,但不满足第x+1个条件。。假如存在满足所有条件的01串,则输出的数字为问题总数。

Sample Input

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
-1

Sample Output

3


#include <cstdio>
#include <map>
#include <cstdlib>
#ifdef Debug
#include <iostream>
#endif
using std::map;
long n;
long m;
std::map<long,long> fa;
std::map<long,long> col;
long l[5010];
long r[5010];
long w[5010];

struct node
{
    long l;
    long r;
    long w;
    node(long _l,long _r,long _w):l(_l),r(_r),w(_w){}
    node(){}
}dm[5010];

long getroot(long l)
{
    if (fa[l]==l) return l;
    long tmp = fa[l];
    fa[l] = getroot(tmp);
    col[l] = (col[l]^col[tmp]);
    return fa[l];
}

void merge(long a,long b,long c)
{
//    getroot(a);
//    col[a] = (c^col[getroot(b)]);
//    fa[fa[a]] = fa[b];

    getroot(a);
    getroot(b);
    long tmp = col[a];
    col[a] = c^col[b];
    col[fa[a]] = col[a]^tmp;
    getroot(a);
    fa[fa[a]] = fa[b];
}

int main()
{
    freopen("parity.in","r",stdin);
    freopen("parity.out","w",stdout);
    freopen("err","w",stderr);
    while (1)
    {
        scanf("%ld",&n);
        if (n == -1)  break;
        scanf("%ld",&m);
//        for (long i=1;i<m*2;i++)
//        {
//            col[i] = 0;
//            fa[i] = i;
//        }
        for (long i=1;i<m+1;i++)
        {
            char t[5];
            scanf("%ld%ld",l+i,r+i);
            scanf("%s",t);r[i]++;
            fa[l[i]] = l[i];
            fa[r[i]] = r[i];
            col[l[i]] = col[r[i]] = 0;
            if (t[0] == 'e')
            {
                w[i] = 0;
                node tmp(l[i],r[i],0);
                dm[i] = tmp;
            }
            else
            {
                w[i] = 1;
                node tmp(l[i],r[i],1);
                dm[i] = tmp;
            }
        }
        bool ok = false;
        for (long i=1;i<m+1;++i)
        {
//            #ifdef Debug
//            std::cerr << "551 " << fa[551] << "\n";
//            #endif
            if (l[i]<0||r[i]-1>n)
            {
                printf("%ld",i-1);
                return 0;
            }
            if (getroot(l[i]) == getroot(r[i]))
            {
                #ifdef Debug
                std::cerr << i << " " << fa[l[i]] << " " << fa[r[i]] << std::endl;
                #endif
                if ((col[r[i]]^col[l[i]])!=w[i])
                {
                    ok = true;
                    printf("%ld\n",i-1);
                    #ifdef Debug
                    std::cerr << " a";
                    #endif
                    break;
                }
            }
            else
            {
                merge(l[i],r[i],w[i]);
            }
        }
        if (!ok) printf("%ld",m);
    }
    return 0;
}

标程:

#include<iostream>
#include<string>
const int BLOCK=10000;
const int HASHTHING=6000;
int hashTable[20000];
int father[20000]; //0..9999 , 10000..19999
int Hash(int number)
{
    int pos=number % HASHTHING;
    while(hashTable[pos]!=-1 && hashTable[pos]!=number) pos=(pos+1)%HASHTHING;
    hashTable[pos]=number;
    return pos;
}
int Find(int x)
{
    int root=x;
    while(root!=father[root]) root=father[root];
    while(x!=root)
    {
        int temp=father[x];
        father[x]=root;
        x=temp;
    }
    return root;
}
void Union(int x,int y)
{
    int p=Fnd(x);
    int q=Find(y);
    if (p!=t)
        father[p]=q;
}
int main()
{
    while(true)
    {
        int len;
        std::cin>>len;
        if(len==-1) break;
 
        int n;
        std::cin>>n;
        memset(hashTable,0xff,sizeof(hashTable));
        for(int i=0;i<20000;++i) father[i]=i;
 
        int count;
        for(count=1;count<=n;++count)
        {
            int a,b;
            std::string signal;
            std::cin>>a>>b>>signal;
            bool even=(signal=="even");
            a=Hash(a-1);
            b=Hash(b);
 
            if(even)
            {
                if(Find(a)==Find(b+BLOCK)) break;
                Union(a,b);
                Union(a+BLOCK,b+BLOCK);
            }else{ //odd
                if(Find(a)==Find(b)) break;
                Union(a,b+BLOCK);
                Union(a+BLOCK,b);
            }
        }
        int ans=count-1;
        {
        int tempa,tempb;std::string tempstr;
        for(++count;count<=n;++count)
            std::cin>>tempa>>tempb>>tempstr;
        }
        std::cout<<ans<<std::endl;
    }
    return 0;
}


其实有一个点没有过,但是其实答案有错呀,给出的区间右界大于最右边了。


本题我采用的方法不是最好的,最好的可以参考标程。都是并查集,但是我的方法比较麻烦。


讲我的方法,我的是用了类似以前OJ讲的,向量的思想。

比较简单,因为判断一个区间的1的奇偶个数,实际上必须知道多个区间(这几个区间相加等于此区间,但不是求并,只能是相加)的奇偶性。

也就是说,把这个区间划分成很多个区间,即如果某个区间确定了,必定是这两个点都在一个集合中。

把每个区间的奇偶性求异或(0表示相同,1表示相反,col[i]表示此点和他的根节点的关系)

(由于他给的是闭区间,所以把它转化成左开右闭的区间,即r+1,就行了)

当压缩路径的时候,就把它的col和更新后的父亲的col异或。


当合并的时候,就先压缩路径,col[fa[a]] = col[a]^w^col[b]。之后压缩的时候自动会更新a的col

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值