0基础FWT详解2(巩固)
FWT巩固1
- FWT巩固1
- 卡常技巧
- 巩固习题
- luogu6097
- CF662C
- luogu4221
FWT巩固1
在 上篇文章 中,我们学习了 F W T FWT FWT,本文将带读者一起做几道题,巩固对 F W T FWT FWT 的使用
卡常技巧
一个常数大的 F W T FWT FWT 是非常不利于做题的,所以我们需要卡常。
作者简单总结了一下几点:
- 能不取模就不取模,取模是非常慢的运算。
- 能不开
long long
就不开。 - 对于固定的小于模数用乘法代替取模。
- 循环展开。
对于第三点,我们使用一种叫 Barrett Reduction 的方法。
这里偷个懒,直接上图片:
代码实现:
const int mod = 1e9 + 7;
const uint64_t m = (1ULL << 64) / mod; // 预计算 m// Barrett Reduction 计算 x % mod
uint32_t barrett_reduce(uint64_t x) {uint64_t q = ((__uint128_t(x) * m) >> 64); // q ≈ floor(x / mod)uint32_t r = x - q * mod; // r = x - q * modreturn r < mod ? r : r - mod; // 调整到 [0, mod)
}
当然有时候不用那么大,可以简化,不一定要用 __uint128_t
类型防止溢出。
巩固习题
luogu6097
link
给定两个长度为 2 n 2^n 2n 的序列 a 0 , a 1 , ⋯ , a 2 n − 1 a_0,a_1,\cdots,a_{2^n-1} a0,a1,⋯,a2n−1 和 b 0 , b 1 , ⋯ , b 2 n − 1 b_0,b_1,\cdots,b_{2^n-1} b0,b1,⋯,b2n−1,你需要求出一个序列 c 0 , c 1 , ⋯ , c 2 n − 1 c_0,c_1,\cdots,c_{2^n-1} c0,c1,⋯,c2n−1,其中 c k c_k ck 满足:
c k = ∑ i & j = 0 i ∣ j = k a i b j c_k=\sum_{\substack{{i \& j=0}\\{i~\mid~ j=k}}} a_i b_j ck=i&j=0i ∣ j=k∑aibj
其中 ∣ ~\mid~ ∣ 表示按位或, & \& &表示按位与。
答案对 1 0 9 + 9 10^9+9 109+9 取模。
注意到第二个就是按位或卷积,第一个则是要求 ∣ i ∣ + ∣ j ∣ = ∣ i ∣ j ∣ |i|+|j|=|i|j| ∣i∣+∣j∣=∣i∣j∣,其中 ∣ x ∣ |x| ∣x∣ 为 x x x 在二进制下 1 1 1 的个数。
我们再开一维记录集合中的元素个数即可。
具体地, a i → a ∣ i ∣ , i , b i → b ∣ i ∣ , i a_i\rightarrow a_{|i|,i},b_i\rightarrow b_{|i|,i} ai→a∣i∣,i,bi→b∣i∣,i,然后对每个 i i i 进行一次 F W T FWT FWT,最后可得 c i , o = ∑ j = 0 i a j , o b i − j , o c_{i,o}=\sum_{j=0}^ia_{j,o}b_{i-j,o} ci,o=∑j=0ia