题目:https://www.acwing.com/problem/content/description/241/
小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≤1e9,M≤10000
输入样例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出样例:
3
解法一:边带权做法:首先考虑数据范围,所以要离散化,通过每个问答我们可以得到这样的信息:如果[l,r]是奇数,sum[r]与sum[l-1]的奇偶性不同,如果为偶数,奇偶性相同,(sum为前缀和数组),我们考虑维护一个数组d[i]表示 i 与 f[i] 的关系是否为同类,0代表同类,1代表不同类,当l和r在一个集合时,因为有同一个祖宗节点,所以它们这时的类别已经定型,如果与回答存在矛盾,可以输出答案,反之,如果不在一个集合,则进行合并,并且确定被合并的祖宗节点的类别,这里可以用异或位运算,我们可以省下许多判断,比如如果存在两个不在同一集合的点x,y,x的祖宗节点a的d[a] = d[x] ^ d[y] ^ query[i].op;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e4+5;
struct node {
int x,y,op;
}query[N];
int d[N<<1],f[N<<1];
vector<int> q;
int find(int x) {
if(x != f[x]) {
int root = find(f[x]);
d[x] ^= d[f[x]];
f[x] = root;
}
return f[x];
}
int main() {
int n,m,a,b;
string op;
cin >> n >> m;
for(int i=0; i< N<<1; i++) f[i] = i;
for(int i=0;i<m;i++) {
int t = 0;
cin >> a >> b >> op;
if(op == "odd") t = 1;
query[i] = {a,b,t};
q.push_back(a-1);
q.push_back(b);
}
sort(q.begin(),q.end());
q.erase(unique(q.begin(),q.end()),q.end());
for(int i=0;i<m;i++) {
auto it = find(q.begin(),q.end(),query[i].x-1);//vector不能用lower_bound
int x = distance(q.begin(),it);
it = find(q.begin(),q.end(),query[i].y);
int y = distance(q.begin(),it);
a = find(x),b = find(y);
if(a == b) {
if((d[x]^d[y]) != query[i].op) {
cout << i;
return 0;
}
}else {
d[a] = query[i].op ^ d[x] ^ d[y];
f[a] = b;
}
}
cout << m;
return 0;
}
解法二:扩展域并查集:我们依然先离散化,将一个点x变成两个点x_even,x_odd表示它们的偶数域和奇数域,其中x_even表示sum[x]为偶数,x_odd表示sum[x]为奇数,当query[i].op为奇数时,若出现x的偶数域和y的偶数域在同一集合,则存在矛盾,若不存在矛盾,则将x_odd的集合与y_even合并,x_even与y_odd合并,若query[i].op为偶数时,若出现x的奇数域和y的偶数域在同一集合,则存在矛盾,若不存在矛盾,则将x_odd的集合与y_odd合并,x_even与y_even合并。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e4+5;
struct node {
int x,y,op;
}query[N];
int f[N<<1];
vector<int> q;
int find(int x) {
if(x == f[x]) return x;
return f[x] = find(f[x]);
}
int main() {
int n,m,a,b;
string op;
cin >> n >> m;
for(int i=0; i< N<<1; i++) f[i] = i;
for(int i=0;i<m;i++) {
int t = 0;
cin >> a >> b >> op;
if(op == "odd") t = 1;
query[i] = {a,b,t};
q.push_back(a-1);
q.push_back(b);
}
sort(q.begin(),q.end());
q.erase(unique(q.begin(),q.end()),q.end());
for(int i=0;i<m;i++) {
auto it = find(q.begin(),q.end(),query[i].x-1);
int x = distance(q.begin(),it);
it = find(q.begin(),q.end(),query[i].y);
int y = distance(q.begin(),it);
if(query[i].op) {
if(find(x) == find(y)) {
cout << i;
return 0;
}
f[find(x)] = f[find(y + N)];
f[find(x + N)] = f[find(y)];
}else {
if(find(x + N) == find(y)) {
cout << i;
return 0;
}
f[find(x)] = f[find(y)];
f[find(x + N)] = f[find(y + N)];
}
}
cout << m;
return 0;
}