题目链接:点击这里
题意:
输入 表示有一个长度为 的 字符串, 表示接下来有 行输入,接下来的 行输入中 表示第 到第 个字符中间 的个数为偶数个; 表示第 到第 个字符中间 的个数为奇数个。若 句话中第 是第一次与前面的话矛盾,则输出 。
思路分析:
如果我们用 数组表示序列 的前缀和,那么在每个回答中:
- 有偶数个 ,等价于 与 奇偶性相同。
- 有奇数个 ,等价于 与 奇偶性不同。
注意,这里没有求出 数组,我们只是把 看作变量。
这样就可以把题目所给出的每次询问转换成奇偶性相同与否的判断,即给定若干个变量和关系,判
定这些关系可满足性的问题。注意,本题的传递关系不止一种:
- 若 与 奇偶性相同, 与 奇偶性也相同,那么 与 奇偶性相同。
- 若 与 奇偶性相同, 与 奇偶性不同,那么 与 奇偶性不同。
- 若 与 奇偶性不同, 与 奇偶性也不同,那么 与 奇偶性相同。
基于上述分析,关系之间具有传递性,故可用并查集来做。另外,还需要将数据离散化,因为 ,但是查询次数 ,实际用到的数据也就 ,所以,将 离散化成数组下标可表示的范围,方便并查集的操作。
边权 为 ,表示 与 奇偶性相同; 为 ,表示 与 奇偶性不同。
在路径压缩时,对 到树根路径上的所有边权做异或(xor)运算,即可得到 与树根的奇偶性关系。
这里假设离散化后 和 的值分别是 和 。
对于题目中的第一种询问:
对于题目中的第二种询问:
代码中将上述两种询问写到一起了,如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int N = 20010;
int n, m;
int p[N], d[N];
map<int,int> mp;
int get(int x) // 在线离散化
{
if(mp.count(x) == 0) mp[x] = ++n;
return mp[x];
}
int find(int x)
{
if(x != p[x])
{
int root = find(p[x]);
d[x] ^= d[p[x]];
p[x] = root;
}
return p[x];
}
int main()
{
scanf("%d%d", &n, &m);
n = 0; // 用于离散化
for(int i = 1; i < N; ++i) p[i] = i; // 初始化
int res = m;
for(int i = 1; i <= m; ++i)
{
int a, b;
char op[6];
scanf("%d%d%s", &a, &b, op);
if(res != m) continue;
a = get(a - 1), b = get(b);
int t = 0; // 询问有偶数个1
if(op[0] == 'o') t = 1; // 询问有奇数个1
int pa = find(a), pb = find(b);
if(pa == pb)
{
if((d[a] ^ d[b]) != t)
res = i - 1;
}
else
{
p[pa] = pb;
d[pa] = d[a] ^ d[b] ^ t;
}
}
printf("%d\n", res);
return 0;
}