题解:用并查集处理到队头1数量的奇偶性,每次合并时候处理,具有传递性,合并ab的时候,找到a的祖先节点pa,pa到b的奇偶性不能直接得出,但能由a到b的奇偶性和a到pa的奇偶性得出,比如a到pb为偶,a到b为偶,得出a到b为偶。再由pa到b和b到pb得出pa到pb奇偶性。
#include<bits/stdc++.h>
using namespace std;
unordered_map<int,int> Hash;
const int N=20010;
int p[N];
int idx;
int d[N];
int ans;
int get(int x) //离散化
{
if(!Hash.count(x)) Hash[x]=++idx;
return Hash[x];
}
int find(int x)
{
if(p[x]!=x)
{
int root=find(p[x]);
d[x]=d[x]^d[p[x]]; //我到队头的奇偶性由我到当前队头奇偶性和当前队头的奇偶性得出
p[x]=root;
}
return p[x];
}
int main()
{
for(int i=0;i<N;i++) p[i]=i;
int n,m;
cin>>n>>m;
ans=m;
for(int i=1;i<=m;i++)
{
int a,b;
string s;
cin>>a>>b>>s;
a=get(a-1); //前缀和思想
b=get(b);
int pa=find(a),pb=find(b);
int t=0;
if(s=="odd"){
t=1;
}
if(pa!=pb)
{
p[pa] = pb;
d[pa] = d[a] ^ d[b] ^ t; //合并,pa到队头的奇偶性由a和b,b到队头的奇偶性得出
}
if (pa == pb) {
if ((d[a] ^ d[b]) != t) { //如果在同一队列,而且二者当前关系和条件不符,矛盾
ans = i - 1;
break;
}
}
}
cout<<ans<<endl;
}
拓展域写法:就是枚举a-1和b前缀和为奇偶的情况去判断。
#include<bits/stdc++.h>
using namespace std;
unordered_map<int,int>M;
const int N=20010;
int p[N*2];
int d[N*2];
int n;
int get(int x)
{
if(!M.count(x)){
M[x]=n++;
}
return M[x];
}
int find(int x){
if(p[x]!=x){
p[x]=find(p[x]);
}
return p[x];
}
int main()
{
int m;
cin>>n>>m;
n=0;
int ans=m;
for(int i=0;i<N*2;i++) p[i]=i;
for(int i=0;i<m;i++)
{
int a,b;
string s;
cin>>a>>b>>s;
a=get(a-1);
b=get(b);
int pa=find(a); //pa为奇数 ppb枚举为偶数
int ppa=find(a+N);
int pb=find(b);
int ppb=find(b+N);
if(s=="even"){ //如果a-1和b之前的数量为偶数,那么他们的奇偶不能在同一并查集中
if(p[pa]==p[ppb]){
ans=i;
break;
}
p[pa]=p[pb]; //偶数的情况,那么他们的枚举奇偶时都要一样,合并
p[ppa]=p[ppb];
}
else{
if(p[pa]==p[pb]){
ans=i;
break;
}
p[pa]=p[ppb];
p[ppa]=p[pb];
}
}
cout<<ans<<endl;
}