题目大意: 给出若干个回答,表示某个区间内 1 1 1 的个数是奇数还是偶数,找到一个最大的 x x x,满足前 x x x 个回答之间相互不矛盾。
题解
显然能进行这样一个转化:假如区间内 1 1 1 的个数是奇数,那么区间异或和就是 1 1 1,否则异或和就是 0 0 0。
设 s u m i sum_i sumi 表示前 i i i 位的异或和,那么每次询问区间 [ l , r ] [l,r] [l,r],相当于看 s u m l − 1 x o r s u m r sum_{l-1}~xor~sum_r suml−1 xor sumr 的值是否满足要求,假如满足要求就无事发生。
假如不满足要求的话,就再看一下 l − 1 l-1 l−1 和 r r r 这两个位置之间之前有没有联系,假如有,那么就找到答案了,假如没有联系,那么让 r r r 以及与 r r r 有关系的位置异或 1 1 1 即可使他们满足要求。
处理完每个回答后,要让 l − 1 l-1 l−1 和 r r r 产生联系,这个用并查集维护即可。因为 n n n 的范围比较大,所以这个并查集是个map。
注:可以发现,两个有联系的位置之间,一个异或了 1 1 1 之后必须使另一个也异或 1 1 1 才能满足之前的要求。
代码如下:
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
#define maxn 5010
int n,m;
map<int,int> sum,f;
int findfa(int x){return x==f[x]?x:f[x]=findfa(f[x]);}
int main()
{
while(scanf("%d",&n),n!=-1)
{
scanf("%d",&m);
sum.clear();f.clear();
int x,y,xx,yy,z,ans=m;char s[5];
for(int i=1;i<=m;i++)
{
scanf("%d %d %s",&x,&y,s);x--;
if(ans!=m)continue;
if(f.find(x)==f.end())sum[x]=0,f[x]=x;
if(f.find(y)==f.end())sum[y]=0,f[y]=y;
xx=findfa(x);yy=findfa(y);z=(s[0]=='o');
if(xx!=yy)
{
if((sum[y]^sum[x])!=z)
{
for(map<int,int>::iterator i=sum.begin(),j=f.begin();i!=sum.end();i++,j++)
if(findfa(j->second)==yy)i->second^=1;
}
f[xx]=yy;
}
else if((sum[y]^sum[x])!=z)ans=i-1;
}
printf("%d\n",ans);
}
}
/*
附赠一组数据
10
10
8 10 odd
1 8 even
2 8 odd
1 10 odd
5 6 even
1 1 even
4 4 even
6 6 even
1 6 even
3 4 odd
-1
ans=5
*/