CF1034E Little C Loves 3 III

发布时间 2023-07-21 08:35:03作者: Ender_32k

太神仙了。

直接子集卷积肯定是不行的,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;
}