数据结构_并查集_奇偶游戏(做法2)

利用带扩展域的并查集解决该题。带边权并查集做法见此

题目奇偶游戏

做法:带扩展域并查集
1、扩展域,顾名思义需要扩展。在此题中,一开始的读入和表示与带边权的相同,将读入转化成元素 x 1 , x 2 . . . x_1,x_2... x1,x2... 之后对于每一个 x x x ,分成 x o d d x_{odd} xodd x e v e n x_{even} xeven 两部分,表示 s u m [ x ] sum[x] sum[x] 是奇数还是偶数。

2、对于给出的一个 r e l ( x , y ) rel(x,y) rel(x,y) ,都可以对应 “ x o d d x_{odd} xodd 推出 y o d d y_{odd} yodd”,“ x e v e n x_{even} xeven 推出 y e v e n y_{even} yeven或者 x o d d x_{odd} xodd 推出 y e v e n y_{even} yeven”,“ x e v e n x_{even} xeven 推出 y o d d y_{odd} yodd”。这些性质具有传递性,把可以互相推出的情况放入一个集合,这个过程可以用并查集维护。

3、这样扩展域的并查集带来的好处:不用在调用 find() 维护别的性质和合并集合的时候维护别的性质,即又回归了并查集最初的样子;这里的思想有一点点像状态机状态转移的感觉。

4、需要离散化,同做法1.

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>

using namespace std;

typedef long long ll;

const int N=10010;

unordered_map<int,int> mp;

ll p[2*N];
int n,m,tot;

struct node{
    int a,b,rel;
}q[2*N];

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

ll find(ll x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}

int main(){
    
    cin>>n>>m;
    
    //cout<<m<<endl;
    
    for(int i=1;i<=m;i++){
        int a,b;
        char s[5];
        scanf("%d%d%s",&a,&b,s);
        q[i]={get(a-1),get(b),s[0]=='o'?1:0};
    }
    //cout<<m<<endl;
    for(int i=1;i<=tot*2;i++) p[i]=i;
    
    //cout<<m<<endl;
    
    for(int i=1;i<=m;i++){
        //cout<<i<<endl;
        int a=q[i].a,b=q[i].b,rel=q[i].rel;
        ll a_odd=a,a_even=a+tot;
        ll b_odd=b,b_even=b+tot;
        if(rel==0){
            if(find(a_odd)==find(b_even)){
                cout<<i-1<<endl;
                return 0;
            }
            p[find(a_odd)]=find(b_odd);
            p[find(a_even)]=find(b_even);
        }
        else {
            if(find(a_odd)==find(b_odd)){
                cout<<i-1<<endl;
                //cout<<"!";
                return 0;
            }
            p[find(a_odd)]=find(b_even);
            p[find(a_even)]=find(b_odd);
        }
    }
    
    cout<<m<<endl;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值