POJ 1733 Parity game(路径压缩并查集+离散化)

273 篇文章 0 订阅
112 篇文章 0 订阅

POJ 1733 Parity game(路径压缩并查集+离散化)

http://poj.org/problem?id=1733

题意:

        有一个由0或1组成的长度为N的串,现在给你K条该串的信息如:

        1 2 even 表示连续字符区间[1,2]之间的字符有偶数个1。

        3 4 odd 表示连续字符区间[3,4]之间的字符有奇数个1。

        现在问你给的K条信息是否存在矛盾。假设存在矛盾且第一条矛盾信息为第i+1条,那么请输出i(即输出最大的前i条不矛盾的信息的这个最大i)。

分析:

        首先对于每个输入的区间[u,v],我们把他改成(u-1,v]这个半开半闭的区间。然后令u-1和v做为并查集中的两个个节点。并查集每个节点维护下列信息:

        F[i]:表示i节点的父节点编号(父节点编号>i)。

        S[i]:表示i节点到其父节点的这个半开半闭区间(i,F[i]]内的1的个数(0表偶数个,1表奇数个)。

        通过findset(u)我们可以让u直接连在根节点下面,并且更新S[u]值表示(u,根]这个区间1的属性。

        如果u-1与v此时不在同一个分量中,那么我们可以知道(u-1,F[u-1]]区间和(v,F[v]]区间1的属性且我们知道(u-1,v]区间的1属性,那么我们可以推算出(F[u-1],F[v]](假设F[u-1]<F[v])区间的属性,进而合并F[u-1]与F[v]的连通分量。注意:合并两个连通分量的时候,我们始终保持编号大的节点作为父亲,编号小的节点作为儿子(虽然下面的代码并没有遵照这个约定,因为就算随便合并也可以,但是需要验证较多情况)。

        至此,本题的并查集部分就完成了。

        由于只有5000条语句,但是却有最大a为10亿,我们并查集没必要去申请10亿的数组。所以我们将预先读入所有输入数据对(u,v,type)且存储(u-1,v,type)这种三元组,然后将所有的u-1和v排序重新从1到X编号(即用map重新映射到数轴上)。然后用上面分析的并查集处理即可。

        举例如下:

        原始输出为:[2 8] even [2 7] odd

        转换之后为:(1,8] even (1,7] odd

        重新映射之后为:(1,3] even (1,2] odd

        原来区间(7,8]之间为odd,现在同样区间(2,3]之间为odd。(2,3]区间不是指整数2到整数3之间而是代值第2个端点(值7)到到第3个端点(值8)之间为odd。 

AC代码:110ms 由map实现哈希表

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int MAXN=10000+100;
int F[MAXN];
int S[MAXN];
int findset(int i)
{
    if(F[i]==-1)return i;
    int temp = findset(F[i]);
    S[i] =S[i]^S[F[i]];
    return F[i]=temp;
}
map<int ,int>mp;
int tot=0;
int insert(int x)
{
    if(mp.find(x)==mp.end())mp[x]=tot++;
    return mp[x];
}
int main()
{
    int n,m,k;
    while(scanf("%d",&n)==1)
    {
        memset(F,-1,sizeof(F));
        memset(S,0,sizeof(S));
        scanf("%d",&m);
        k=m;
        for(int i=0; i<m; i++)
        {
            int u,v;
            char str[10];
            scanf("%d%d%s",&u,&v,str);
            if(k<m)continue;
            u=insert(u-1);
            v=insert(v);
            int temp;
            if(str[0]=='e')//获取(u,v]之间的奇偶性
                temp=0;
            else if(str[0]=='o')
                temp=1;

            int fu=findset(u);
            int fv=findset(v);
            if(fu==fv)
            {
                int t = S[u]^S[v];
                if(t!=temp)
                    k=min(k,i);
            }
            else if(fu!=fv)
            {
                F[fu]=fv;
                S[fu]=S[u]^S[v]^temp;
            }
        }
        printf("%d\n",k);
    }
    return 0;
}</span>


AC代码: 32ms由HASHMAP实现

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=10000+100;
int F[MAXN];
int S[MAXN];
int findset(int i)
{
    if(F[i]==-1)return i;
    int temp = findset(F[i]);
    S[i] =S[i]^S[F[i]];
    return F[i]=temp;
}
/*
//********哈希模板①*********
#include<map>
map<int ,int>mp;
int tot=0;
int insert(int x)
{
    if(mp.find(x)==mp.end())mp[x]=tot++;
    return mp[x];
}
//********哈希模板①*********
*/
//********哈希模板②*********
const int HASH=10007;
struct HASHMAP
{
    int head[HASH];
    int next[MAXN];
    int state[MAXN];
    int size;
    void init()
    {
        memset(head,-1,sizeof(head));
        size=0;
    }
    int push(int st)
    {
        int h=st%HASH;
        for(int i=head[h];i!=-1;i=next[i])
        {
            if(state[i]==st)
            {
                return i;
            }
        }
        state[size]=st;
        next[size]=head[h];
        head[h]=size++;
        return size-1;
    }
}hm;
//********哈希模板②*********

int main()
{
    int n,m,k;
    while(scanf("%d",&n)==1)
    {
        hm.init();
        memset(F,-1,sizeof(F));
        memset(S,0,sizeof(S));
        scanf("%d",&m);
        k=m;
        for(int i=0; i<m; i++)
        {
            int u,v;
            char str[10];
            scanf("%d%d%s",&u,&v,str);
            if(k<m)continue;
            u=hm.push(u-1);
            v=hm.push(v);
            int temp;
            if(str[0]=='e')//获取(u,v]之间的奇偶性
                temp=0;
            else if(str[0]=='o')
                temp=1;

            int fu=findset(u);
            int fv=findset(v);
            if(fu==fv)//相同连通分量,求值
            {
                int t = S[u]^S[v];
                if(t!=temp)
                    k=min(k,i);
            }
            else if(fu!=fv)//不同连通分量,合并
            {
                F[fu]=fv;
                S[fu]=S[u]^S[v]^temp;
            }
        }
        printf("%d\n",k);
    }
    return 0;
}
</span>

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值