题目链接:【清华集训 2014】玛里苟斯
推荐博客:【BZOJ 3811】玛里苟斯:线性基(详细证明)
首先想到将 k k 分类讨论。
时,我们考虑每一位的贡献。若有至少一个数第 i i 位为,则对答案的贡献为 valuei2 v a l u e i 2 。
k=2 k = 2 时,发现每个异或和的平方为 ∑i∑j2i+jbitibitj ∑ i ∑ j 2 i + j b i t i b i t j 。那么考虑第 i i 位和第位的积的期望值。如果所有的数中,第 i i 位和第位均相等且非全零,那么参考 k=1 k = 1 的情况,期望为 12 1 2 ;否则,第 i i 位为的概率为 12 1 2 ,第 j j 位为的概率为 12 1 2 , i×j i × j 为 1 1 的概率为。
k≥3 k ≥ 3 时, 由于答案不超过 263 2 63 ,所以每个数不超过 221 2 21 ,所以线性基个数不超过 21 21 ,则可以暴力枚举异或和来计算答案了。注意精度问题。
我怀疑我学了假的线性基模版···
#include <cstdio>
#include <iostream>
const int maxn = 100005;
typedef unsigned long long ull;
int n, m, k;
ull a[maxn], base[maxn], b[maxn];
void solve1() {
ull ans = 0;
for (int i = 1; i <= n; i++) {
ans |= a[i];
}
printf("%llu", ans >> 1);
if (ans & 1) {
printf(".5");
}
putchar('\n');
}
void solve2() {
ull ans = 0, res = 0;
for (int i = 32; i >= 0; i--) {
for (int j = 32; j >= 0; j--) {
bool flag0 = 0, flag1 = 0, flag = 0;
for (int k = 1; k <= n; k++) {
flag0 |= a[k] >> i & 1;
flag1 |= a[k] >> j & 1;
flag |= (a[k] >> i & 1) != (a[k] >> j & 1);
}
if (!flag0 || !flag1) {
continue;
}
if (i + j - flag - 1 < 0) {
res++;
} else {
ans += 1ull << (i + j - flag - 1);
}
}
}
ans += res >> 1;
printf("%llu", ans);
if (res & 1) {
printf(".5");
}
putchar('\n');
}
void solve3() {
ull ans = 0, res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 22; j >= 0; j--) {
if (a[i] >> j & 1) {
if (base[j]) {
a[i] ^= base[j];
} else {
base[j] = a[i];
b[++m] = a[i];
break;
}
}
}
}
for (int i = 0; i < 1 << m; i++) {
ull val = 0;
for (int j = 1; j <= m; j++) {
if (i >> (m - j) & 1) {
val ^= b[j];
}
}
ull a = 0, b = 1;
for (int j = 1; j <= k; j++) {
a *= val, b *= val;
a += b >> m, b &= (1 << m) - 1;
}
ans += a, res += b;
ans += res >> m, res &= (1 << m) - 1;
}
printf("%llu", ans);
if (res) {
printf(".5");
}
putchar('\n');
}
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%llu", a + i);
}
if (k == 1) {
solve1();
} else if (k == 2) {
solve2();
} else {
solve3();
}
return 0;
}