0基础FWT详解2(巩固)

发布于:2025-05-01 ⋅ 阅读:(39) ⋅ 点赞:(0)

FWT巩固1

上篇文章 中,我们学习了 F W T FWT FWT,本文将带读者一起做几道题,巩固对 F W T FWT FWT 的使用

卡常技巧

一个常数大的 F W T FWT FWT 是非常不利于做题的,所以我们需要卡常。

作者简单总结了一下几点:

  1. 能不取模就不取模,取模是非常慢的运算。
  2. 能不开 long long 就不开。
  3. 对于固定的小于模数用乘法代替取模。
  4. 循环展开。

对于第三点,我们使用一种叫 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 * mod
    return 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,,a2n1 b 0 , b 1 , ⋯   , b 2 n − 1 b_0,b_1,\cdots,b_{2^n-1} b0,b1,,b2n1,你需要求出一个序列 c 0 , c 1 , ⋯   , c 2 n − 1 c_0,c_1,\cdots,c_{2^n-1} c0,c1,,c2n1,其中 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=kaibj

其中   ∣   ~\mid~   表示按位或, & \& &表示按位与。

答案对 1 0 9 + 9 10^9+9 109+9 取模。

注意到第二个就是按位或卷积,第一个则是要求 ∣ i ∣ + ∣ j ∣ = ∣ i ∣ j ∣ |i|+|j|=|i|j| i+j=ij,其中 ∣ 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} aiai,i,bibi,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