看到这道题,然后便错误地联想到了数位DP什么的……接着发现了答案在一个界内,然后就想着暴力了。
首先如果对每一位都设一个变量的话,根据插板法,最多只能有1e7个项对答案有贡献,和答案在一个界内这个条件组合起来,这启发我们去想暴力。
首先非基变量是没有用的,考虑它们的选和不选,最后都要除掉。
所以只有基变量有贡献。
显然基变量越多跑的越慢,这个时候设基变量有 (b) 个,很明显可以得到答案的一个粗略的下界
[frac{sum_{i=0}^{2^{b-1}} i^k}{2^{b-1}}
]
首先上面是一个自然幂数前缀和,显然是一个 (k+1) 项的多项式,忽略常数带来的影响,除掉分母,令 (x = 2^b),可得
[x ^ k le 2^{63}
]
也就是说,$ b leq log_2 sqrt[k]{2^{63}} $,易得 (k geq 3) 的时候是可以暴力枚举基的。
那么只剩下 (k = 1) 或 (2) 了。
考虑 (k = 1),只要按位考虑贡献即可。
考虑 (k = 2),对于平方,只是同一种方案对每两个位的值进行乘积后求和。所以需要同时考虑两位。
因为单个变量 (0) 和 (1) 是相同的,所以考虑分别乘个 (frac{1}{2}) 即可。
拆一下式子枚举变量即可。注意 (k = 2) 时只有 (frac{1}{2}) 的判定不是下标相同(虽然式子拆出来是这样的),而是在基张成的线性空间里每个向量这两位都相同。即基向量矩阵中这两行相等。
计算过程可以用 __int128 所以没什么问题,long double 会掉精度。
所以还是一道简单题,但我还是爆OJ了。
#include <bits/stdc++.h>
typedef unsigned long long LL;
typedef __int128 H;
LL lb[64], bss[64], mat[64];
int n, K, bak;
void insert(LL v) {
for (int i = 63; ~i; --i)
if (v >> i & 1) {
if (lb[i]) v ^= lb[i];
else { lb[i] = v; break; }
}
}
LL fnd(LL x) {
LL res = 0;
for (int i = 63; ~i; --i)
if (lb[i] & x)
res |= 1llu << i;
return res;
}
H fastpow(H a, int px) {
H res = 1;
while (px --> 0) res *= a;
return res;
}
H haans;
void dfs(int now, LL v) {
if (now == bak) {
haans += fastpow(v, K);
return ;
}
dfs(now + 1, v);
dfs(now + 1, v ^ bss[now]);
}
int main() {
std::ios_base::sync_with_stdio(false), std::cin.tie(0);
std::cin >> n >> K;
LL t;
for (int i = 1; i <= n; ++i) std::cin >> t, insert(t);
for (int i = 0; i < 64; ++i) if (lb[i]) bss[bak++] = lb[i];
H ans = 0; LL app = 0;
for (int i = 0; i != 64; ++i) app |= lb[i];
if (K >= 3) dfs(0, 0), ans = haans >> bak - 1;
else if (K == 2) {
for (int i = 0; i != 64; ++i) mat[i] = fnd(1llu << i);
for (int i = 0; i != 64; ++i) if (app >> i & 1)
for (int j = 0; j != 64; ++j) if (app >> j & 1)
ans += (H) (1llu << i) * (1llu << j) * (1 << (mat[i] == mat[j]));
ans >>= 1;
} else ans = app;
LL ax = ans;
std::cout << (ax >> 1);
if (ax & 1) std::cout << ".5";
std::cout << std::endl;
return 0;
}