这把牛客多校的A和K都不会(第四和第五题),但是发现B和C都很简单(虽然过的人比较少),过了5题。发现自己对FWT等多项式算法的理解还不是很够,写一个博客记录一下。
题意
一个 n ( n ≤ 2 ∗ 1 0 5 ) n(n\le 2*10^5) n(n≤2∗105) 个结点的树上面有 m ( m ≤ 22 ) m(m\le 22) m(m≤22) 特别的边。给定 k ( k ≤ 2 ∗ 1 0 5 ) k(k\le 2*10^5) k(k≤2∗105) 条路径,问你从这 k k k 条路径中最少选出多少条能够覆盖调所有的特别边,并求出方案数。
思路
转换成二进制,然后题意等价于从 k k k 个整数里面选择最少的数使得全部或起来是 2 m − 1 2^m-1 2m−1
然后可以写成 min t [ x 2 m − 1 ] ( ∑ i = 0 2 m − 1 a i x i ) t > 0 \min\limits_{t}[x^{2^m-1}](\sum\limits_{i=0}^{2^m-1}a_ix^i)^t>0 tmin[x2m−1](i=0∑2m−1aixi)t>0
定义 x i × x j = x i ∣ j x^i\times x^j=x^{i|j} xi×xj=xi∣j, a i a_i ai 为路径中覆盖集合为 i i i 的个数。
然后只需要先对 ∑ i = 0 2 m − 1 a i x i \sum\limits_{i=0}^{2^m-1}a_ix^i i=0∑2m−1aixi 进行一次 FWT,然后判断最少乘几次以后最高次项为 > 0 >0 >0,让后对其系数乘上 1 t ! \frac{1}{t!} t!1 即可
这样的复杂度是 O ( m 2 2 m ) O(m^22^m) O(m22m) 的,会超时
考虑使用二分,可以优化到 O ( m 2 m log m ) O(m2^m\log m) O(m2mlogm) 貌似已经可以通过。
因为我们只需要求出最高次项的系数,因此可以研究一下 iFWT 的过程。
void iOR(int *f) {
for (int o = 2, k = 1; o <= lim; o <<= 1, k <<= 1) {
for (int i = 0; i < lim; i += o) {
for (int j = 0; j < k; j++) {
f[i + j + k] = (f[i + j + k] - f[i + j] + mod) % mod;
}
}
}
}
每一项对最高次项的系数的贡献都是 − 1 -1 −1 或者 1 1 1,如果其二进制有一个 0 0 0,就会反转一次。
因此总的贡献就是 ∑ i = 0 2 m − 1 ( − 1 ) p o p c n t ( i ⊕ m a s k ) f i \sum\limits_{i=0}^{2^m-1}(-1)^{popcnt(i\oplus mask)}f_i i=0∑2m−1(−1)popcnt(i⊕mask)fi,其中 m a s k = 2 m − 1 mask=2^m-1 mask=2m−1