CF1034E Little C Loves 3 III
太神仙了。
直接子集卷积肯定是不行的,1s 的时限和 62MB 的空间摆在那里。
那就要考虑使用模 \(4\) 的性质乱搞了。
我们考虑给每个 \(i\),不管它符不符合条件,赋一个权值。如果 \(i\ \text{and}\ j\neq 0\),它对答案是没有贡献的,否则它能贡献到 \(i\ \text{or}\ j\) 的位置。
那考虑所有没贡献 \((i,j)\),我们想装模做样地给它贡献到 \(i\ \text{or}\ j\),就需要 \(i\) 和 \(j\) 的权值“抵消”掉。下面是一个十分精妙的想法:
由于 \(i\ \text{and}\ j\neq 0\),那么 \(|i|+|j|\ge|i\ \text{or}\ j|\)。我们可以给每个 \(a_i\) 赋为 \(a_i\times4^{\text{popcount(i)}}\),\(b\) 同理,我们计算 \(c_k=\sum\limits_{i\ \text{or}\ j=k}a_ib_j\),那么 \(c_k\) 中 \(i\ \text{and}\ j\) 不为 \(0\) 的项的乘积必然能被 \(4^{\text{popcount(k)+1}}\) 整除,不做贡献,所以最后 \(c_k\) 直接除 \(4^{\text{popcount(k)}}\) 再模 \(4\) 就是正确的答案。
显然对于合法的 \(i,j\),对 \(c_k\) 的贡献按照上述方法计算,还是单次的。
然后就做完了,计算 \(c\) 需要 FWT,所以复杂度 \(O(n2^n)\)。
太神仙了。
const int T = 21;
int n, S, a[(1 << T) + 100], b[(1 << T) + 100];
char s[(1 << T) + 100], t[(1 << T) + 100];
void fwt(int *s, int op) {
for (int o = 2, k = 1; o <= S + 1; o <<= 1, k <<= 1)
for (int i = 0; i <= S; i += o)
for (int j = 0; j < k; j++)
s[i + j + k] += s[i + j] * op;
}
signed main() {
n = read(), S = (1 << n) - 1;
scanf("%s%s", s, t);
for (int i = 0; i <= S; i++) a[i] = ((15ll & s[i]) << (__builtin_popcount(i) << 1));
for (int i = 0; i <= S; i++) b[i] = ((15ll & t[i]) << (__builtin_popcount(i) << 1));
fwt(a, 1), fwt(b, 1);
for (int i = 0; i <= S; i++) a[i] *= b[i];
fwt(a, -1);
for (int i = 0; i <= S; i++) write((a[i] >> (__builtin_popcount(i) << 1)) & 3);
return 0;
}