题意:现在有一个长度为n的零一串,给出m次查询,每次查询格式为:从第u位到第v位的1的个数为奇数或者偶数,其中
“even” 是偶数的意思“odd” 是奇数的意思。问,前几个查询是正确的,也就是第一个错误的查询的前一条查询是第几次查询。
思路:带权并查集,权值为 本节点到父亲节点的1的个数的奇偶性,
偶数为0,奇数为1,由于n很大,我们开不了这么大的数组,但是m只有5000,所以我们考虑压缩一下。 看代码操作
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
const int Max=10005;
struct node//记录父亲节点和权值
{
int par,relation;
};
node p[Max];
map<int ,int > mmp;//数据比较大,所以用map压缩
int n,m,cnt,ans,flag;
void init()
{
flag=cnt=0;
mmp.clear();
for(int i=1;i<Max;i++){
p[i].par=i;
p[i].relation=0;
}
}
int Hash(int x)//hash,压缩函数
{
if(mmp.find(x)==mmp.end())//对应数字出现的顺序,由于查询只有5000次,所最多会出现10000个数字
mmp[x]=cnt++; //这样就将1e9的数据压缩到了1e5的范围里面
return mmp[x];
}
int Find(int x)//查找函数
{
if(x==p[x].par)
return x;
int tmp=p[x].par;
p[x].par=Find(tmp);
p[x].relation^=p[tmp].relation;//权值的转移
return p[x].par;
}
bool unite(int x,int y,int c)
{
int root1=Find(x);
int root2=Find(y);
if(root1!=root2){//根不同,将y的根连到x的根上面
p[root2].par=root1;
p[root2].relation=c^p[x].relation^p[y].relation;//权值的转移
}
else{//根相同则直接判断是否正确
if(c!=p[x].relation^p[y].relation)
return 0;
}
return 1;
}
int main()
{
int u,v,tmp;
char str[10];
while(cin>>n>>m){
init();
for(int i=0;i<m;i++){
cin>>u>>v>>str;
if(u>v) swap(u,v);;
u=Hash(u-1);
v=Hash(v);
if(str[0]=='e') tmp=0;
else tmp=1;
if(!flag&&!unite(u,v,tmp)){
ans=i;
flag=1;
}
}
if(!flag)//需要注意的是,如果所有的查询都正确,那么直接输出查询的次数即可
cout<<m<<endl;
else
cout<<ans<<endl;
}
return 0;
}