小 A 和小 B在玩一个游戏。首先,小 A写了一个由 0 和 1 组成的序列 S,长度为 N。然后,小 B向小 A 提出了 M个问题。在每个问题中,小 B指定两个数 l 和 r,小 A 回答 S[l∼r] 中有奇数个 1 还是偶数个 1。机智的小 B发现小 A有可能在撒谎。例如,小 A曾经回答过 S[1∼3] 中有奇数个 1,S[4∼6] 中有偶数个 1,现在又回答 S[1∼6] 中有偶数个 1,显然这是自相矛盾的。请你帮助小 B检查这 M 个答案,并指出在至少多少个回答之后可以确定小 A一定在撒谎。即求出一个最小的 k,使得 01 序列 S 满足第 1∼k 个回答,但不满足第 1∼k+1个回答。
输入格式
第一行包含一个整数 N,表示 01序列长度。第二行包含一个整数 M,表示问题数量。接下来 M行,每行包含一组问答:两个整数 l 和 r,以及回答 even
或 odd
,用以描述 S[l∼r] 中有偶数个 1 还是奇数个 1。
输出格式
输出一个整数 k,表示 01 序列满足第 1∼k 个回答,但不满足第 1∼k+1 个回答,如果 01序列满足所有回答,则输出问题总数量。
数据范围
N≤10e9,M≤5000
输入样例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出样例:
3
思路:
前缀和+异或+并查集+离散化
用sum数组表示前缀和,得到以下结论:
s[l~r]有偶数个1,等价于sum[l-1]与sum[r]奇偶性相同
s[l~r]有奇数个1,等价于sum[l-1]与sum[r]奇偶性不同
这道题的奇偶性具有传递性。
由于N很大,所以要离散化。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 20010;
int n,m;
int p[N],d[N];
unordered_map<int,int> S;
int get(int x){
if(S.count(x)==0)S[x]=++n;
return S[x];
}
int find(int x){
if(p[x]!=x){
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
int main()
{
cin >> n >> m;
n = 0;
for (int i = 0; i < N; i ++ ) p[i] = i;
int res = m;
for (int i = 1; i <= m; i ++ )
{
int a, b;
string type;
cin >> a >> b >> type;
a = get(a - 1), b = get(b);
int t = 0;
if (type == "odd") t = 1;
int pa = find(a), pb = find(b);
if (pa == pb)
{
if (((d[a] + d[b]) % 2 + 2) % 2 != t)
{
res = i - 1;
break;
}
}
else
{
p[pa] = pb;
d[pa] = d[a] ^ d[b] ^ t;
}
}
cout << res << endl;
return 0;
}