Codeforces 799F Beautiful fountains rows

题面在这里

题意:有一\(N\times M\)的01矩阵,第i行的\([l_i,r_i]\)为1,区间\([L,R]\)是合法的当且仅当每一行在\([L,R]\)中出现奇数次,求所有合法区间的长度和

首先考虑\([L,R]\)合法,每一行都需要出现奇数次

这个看起来可以用异或乱搞

给每行赋一个随机权值,看作许多在一维空间分布的带权线段

通过差分可以得到以下数组:

  • \(a[i]\):所有在i位置出现过奇数次的线段异或和
  • \(s[i]\)\(a[i]\)的前缀异或和
  • \(A[i]\):左端点为i的线段异或和
  • \(d[i]\)\(A[i]\)的前缀异或和(左端点小于等于i的线段异或和)

对于区间\([L,R]\),如果\(s[R]\text{^}s[L-1]=S\),其中S表示区间中出现过的线段异或和,就可以认为是合法的

\(d[R]\text{^}d[L]\)表示\(l_i \in (L,R]\)的线段异或和

那么\(d[R]\text{^}d[L]\)比S少了\(l_i\le R,r_i\ge L\)的线段,其实少了\(a[L]\)

那么\(S=d[R]\text{^}d[L-1]\text{^}a[L]=s[R]\text{^}s[L-1]\)

移相整理得\(s[L]\text{^}d[L]=s[R]\text{^}d[R]\)

用MAP搞之

注意这样统计其实会把没有线段的区间判定为合法

最后减去空区间即可

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
#define c(x) ((x)*((x)+1)/2)

const int maxn=200005;
int n,m;
ull a[maxn],A[maxn],s[maxn],d[maxn],ans;
map<ull,ull> num,sum;
int main(){
srand(998244353);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
int x,y;scanf("%d%d",&x,&y); ull w=(ull)rand()*rand()*rand()+(ull)rand()*rand()+rand();
a[x]^=w;a[y+1]^=w; A[x]^=w;
}
for (int i=1;i<=m;i++)
a[i]^=a[i-1],s[i]=s[i-1]^a[i],d[i]=d[i-1]^A[i];
for (int i=1;i<=m;i++){
ull w=s[i]^d[i];
num[w]++;sum[w]+=i-1;
ans+=num[w]*i-sum[w];
}
for (int i=1,j=0;i<=m;i++)
if (a[i]==0) ans-=c((ull)i-j);
else j=i;
printf("%lld",ans);
return 0;
}