奇偶游戏

原题链接

法一:带权并查集

分析:

通过分析题目,假设s[i]为前缀和数组,我们可以得到以下等效关系:

[l,r]内1的个数 <=> s[r]-s[l-1] <=> 若为奇数,则s[l],s[r]奇偶性不同,若为偶数则相同

因此我们可以用带权并查集来解决这个问题。

我们在用带权并查集时,是维护了一个相对关系,通过与根节点的距离来判断与根节点是否同类。

距离mod2=1则不同类,mod2=0则同类

我们设d[x]为x到px的距离。同时补充一个知识点,在mod2的情况下进行加法,就是进行不进位的加法,即异或运算。

接下来进行分类讨论

1.x,y是同一类
  1. px=py 则两者距离根节点的距离相加应该为偶数,在mod2的意义下,则应该是dx^dy=0,是无矛盾的,若为1则有矛盾
  2. px!=py 则d[px]+d[x]+d[y]=偶数,则在mod2的意义下,应该为d[px]d[x]d[y]=0,即d[px]=d[x]d[y]0;
2.x,y是不同类
  1. px=py 则两者距离根节点的距离相加应该为奇数,在mod2的意义下,则应该是dx^dy=1,是无矛盾的,若为0则有矛盾
  2. px!=py 则d[px]+d[x]+d[y]=奇数,则在mod2的意义下,应该为d[px]d[x]d[y]=1,即d[px]=d[x]d[y]1;

ACcode

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int p[N],d[N];
unordered_map<int,int> mp;
int n,m;

int get(int x)
{
    if(!mp.count(x)) mp[x]=++n;
    return mp[x];
}

int find(int x)  // 并查集
{
    if (p[x] != x) 
    {
        int root = find(p[x]);
        d[x] ^=d[p[x]];
        p[x]=root;
    }
    return p[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    n=0;
    for(int i=1;i<=N;i++) p[i]=i;
    int res = m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        string type;
        cin>>a>>b>>type;
        a = get(a-1),b = get(b);
        int pa = find(a),pb = find(b);
        
        int t = 0;
        if(type=="odd") t=1;
        
        if(pa==pb)
        {
            if(d[a]^d[b]!=t)
            {
                res = i-1;
                break;
            }
        }
        else 
        {
            p[pa]=pb;
            d[pa]=d[a]^d[b]^t;
        }
    }
    cout<<res<<endl;
    return 0;
}

法二:扩展域

分析:

扩展域,利用的是一种枚举的思想,通过枚举所有条件的可能性,来推论是否有矛盾。

x->x为偶数 x+n->x为奇数

再一个集合中的所有条件是互相可推导的。

1.x,y是同类
  1. x,y必须奇偶性相同,因此我们需要判断,是否能从x+n推导出y,或者从y+n推导出x,若能,则矛盾。
  2. 若没找到,则将x,y放到同一个集合,x+n与y+n放到同一个集合
2.x,y是异类
  1. x,y必须奇偶性不相同,因此若能从x推出y,或x+n推出y+n则矛盾
  2. 若是无矛盾,则将x与y+n放到同一集合,将y+n与x放到同一集合

ACcode

#include<bits/stdc++.h>
using namespace std;
const int N = 4e4 + 10;
int p[N];
unordered_map<int,int> mp;
int n,m;

int get(int x)
{
    if(!mp.count(x)) mp[x]=++n;
    return mp[x];
}

int find(int x)  // 并查集
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    int idx=N/2;
    n=0;
    for(int i=1;i<=N;i++) p[i]=i;
    int res = m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        string type;
        cin>>a>>b>>type;
        a = get(a-1),b = get(b);
        if(type=="even")
        {
            if(find(a+idx)==find(b))
            {
                res = i-1;
                break;
            }
            p[find(a)]=p[find(b)];
            p[find(a+idx)]=p[find(b+idx)];
        }
        else
        {
            if(find(a)==find(b))
            {
                res = i - 1;
                break;
            }
            p[find(a+idx)]=p[find(b)];
            p[find(b+idx)]=p[find(a)];
        }
    }
    cout<<res<<endl;
    return 0;
}
posted @ 2021-08-23 10:43  艾特玖  阅读(97)  评论(0)    收藏  举报