题目描述
有一个长度为 N(N≤109) N ( N ≤ 10 9 ) 的 01 01 序列,共给你 M(M≤10000) M ( M ≤ 10000 ) 条信息,每条信息给出 [l,r] [ l , r ] 中 1 1 的个数的奇偶性,但只有前 条信息是正确的,请输出 k k 的值。
算法分析
将信息拆成前缀和的形式,即 的奇偶性。
奇偶性有一个类比异或的性质:
相加的两个加数奇偶性相同时,运算结果为偶数;相加的两个加数奇偶性不同时,运算结果为奇数。
对应异或的性质是:
两个位相同时,异或的结果为 0 0 ;两个位不同时,异或的结果为 。
则上述式子等价于:
若 sumr−suml−1=0 s u m r − s u m l − 1 = 0 , suml−1 s u m l − 1 和 sumr s u m r 的奇偶性相同;若 sumr−suml−1=1 s u m r − s u m l − 1 = 1 , suml−1 s u m l − 1 和 sumr s u m r 的奇偶性不同。
这个我们用带权并查集维护,具体实现为:
- 当 suml−1 s u m l − 1 和 sumr s u m r 在同一集合中时,判断两者到根节点的路径的异或值是否与给出的信息不同,如果不同则发现矛盾。
- 当 suml−1 s u m l − 1 和 sumr s u m r 不在同一集合中时,将其中一个节点的根节点合并到另一棵树中,权值为两个节点到各自根结点的距离与信息给出奇偶性的异或和。
当然,由于 N N <script type="math/tex" id="MathJax-Element-21">N</script> 的取值范围较大,我们需要先进行离散化。
代码实现
#include <cstdio>
#include <algorithm>
const int maxm=5005;
struct opt {
int l,r,t;
} a[maxm];
int hash[2*maxm],idx=0;
inline int query(int x) {return std::lower_bound(hash,hash+idx,x)-hash;}
int fa[2*maxm],d[2*maxm];
int find(int x) {
if(x==fa[x]) return x;
int rt=find(fa[x]);
d[x]^=d[fa[x]];
return fa[x]=rt;
}
int main() {
int n,m;scanf("%d%d",&n,&m);
char s[10];
for(int i=0;i<m;++i) {
scanf("%d%d%s",&a[i].l,&a[i].r,s);a[i].t=(s[0]=='o');
hash[idx++]=a[i].l-1;hash[idx++]=a[i].r;
}
std::sort(hash,hash+idx);idx=std::unique(hash,hash+idx)-hash;
for(int i=0;i<idx;++i) {
fa[i]=i;d[i]=0;
}
for(int i=0;i<m;++i) {
int l=query(a[i].l-1),r=query(a[i].r);
int x=find(l),y=find(r);
if(x==y) {
if((d[l]^d[r])!=a[i].t) {
printf("%d\n",i);
return 0;
}
}
else {
d[x]=a[i].t^d[l]^d[r];
fa[x]=y;
}
}
printf("%d\n",m);
return 0;
}