二进制位运算相关的计数问题——巧用高维前缀和:0922T2

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/133183403

http://cplusoj.com/d/senior/p/SS230922B

https://blog.csdn.net/zhangtingxiqwq/article/details/133176573 当中,我们大致对题目进行了转化。

对于询问 kk ,我们现在要求所有 a(i,j)a(i,j) 的异或和,满足 k&(ij)=(ij)k\&(i|j)=(i|j)

对于这个东西,有个常见的套路,叫高维前缀和

首先 iji|j 的合法 kk 必然为 iji|j ,而 iji|j 的所有补集都是合法的,这个是可以用高维前缀和做的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = rng.gen(aw);
s[i|j]^=a[i][j];
}
}
for(int k=0; k<=22; ++k) {
for(int i=0; i<(1ll<<23); ++i)
if((i&(1ll<<k))) {
s[i]^=s[i-(1ll<<k)];
}
}

unsigned long long res = 0;
for (int i = 1; i <= q; i++) {
unsigned long long j=rng.gen(kw);
j&=(unsigned long long)((1ll<<23)-1);
res ^= (unsigned long long)i * s[j];
}
printf("%llu\n", res);