题意:现在有一个长度为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; }