利用带扩展域的并查集解决该题。带边权并查集做法见此
题目:奇偶游戏
做法:带扩展域并查集
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;
}