题意:有一个长度已知的01串,给出[l,r]这个区间中的1是奇数个还是偶数个,给出一系列语句问前几个是正确的
思路:一类经典的并查集题目,经典模型就是将[l,r]这个区间化为(l-1,r],那么1的个数就可以表示为sum[r]-sum[l-1],也就确定了奇偶性,我们可以用r[]数组表示这个端点到它的根节点的1的奇偶(这个区间就是(i,root(i)](0代表偶,1代表奇) 对于每个输入的区间,我们查找它们的根节点是否相同
如果相同,证明这个区间的奇偶性在之前已经得知,那么直接判断即可
如果不同,那么就是u-1与v此时不在同一个集合中,那么我们可以知道(u-1,root([u-1])]区间和(v,root([v])]区间1的奇偶,并且我们知道了(u-1,v]区间1的奇偶,那么就可以推算出(root([u-1]),root([v])]区间的属性,进而合并两者。在合并时,根节点,r[root(u)]=r(u)^r(v)^r(u-1,v], 在路径压缩过程中r[i]=r[i]^r[root(i)],比如(a,b]中1的个数为偶数,(b,c]中1的个数为奇数,(a,c]中1的个数显然为奇数
注意:这题目的点最大值高达10^9,而语句只有5000条,所以需要离散化一下
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 5005
#define LL long long
int cas=1,T;
struct Node
{
int u,v;
char str[10];
}que[maxn];
int a[maxn*2],n,q,cnt;
int pre[maxn*2],r[maxn*2];
int Find(int x)
{
if (x!=pre[x])
{
int f = pre[x];
pre[x] = Find(pre[x]);
r[x] = r[x]^r[f];
}
return pre[x];
}
int main()
{
while (scanf("%d",&n)!=EOF)
{
scanf("%d",&q);
cnt=0;
for (int i = 0;i<q;i++)
{
scanf("%d%d%s",&que[i].u,&que[i].v,&que[i].str);
que[i].u--;
a[cnt++]=que[i].u;
a[cnt++]=que[i].v;
}
sort(a,a+cnt);
cnt = unique(a,a+cnt)-a;
for (int i = 0;i<cnt;i++)
{
pre[i]=i;
r[i]=0;
}
int ans = 0;
for (int i = 0;i<q;i++)
{
int u = lower_bound(a,a+cnt,que[i].u)-a;
int v = lower_bound(a,a+cnt,que[i].v)-a;
int ra = Find(u);
int rb = Find(v);
if (ra == rb)
{
if (r[u]==r[v] && que[i].str[0]=='o')
break;
if (r[u]!=r[v] && que[i].str[0]=='e')
break;
ans++;
}
else
{
if (que[i].str[0]=='o')
{
pre[ra]=rb;
r[ra]=r[u]^r[v]^1;
}
else
{
pre[ra]=rb;
r[ra]=r[u]^r[v];
}
ans++;
}
}
printf("%d\n",ans);
}
//freopen("in","r",stdin);
//scanf("%d",&T);
//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}