1 条题解
-
0

#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e6 + 10; const LL P = 998244353; LL a[N], b[N], c[N]; int n; // zeta 变换:对数组进行“倍数和”变换 // 作用:将 a[i] 变为 sum_{d 是 i 的倍数} a[d](即倍数前缀和) // 等价于:a[i] = Σ_{j=1}^{⌊n/i⌋} a[i*j] // 这是迪利克雷卷积中的“zeta 变换”(在整除格上) void zeta(LL a[]) { for (int i = 1; i <= n; i ++) { // 从小到大,这样保证用到的都是原先的 a 数组 for (int j = 2; j <= n / i; j ++) { a[i] = (a[i] + a[i * j]) % P; } } } // Möbius变换:zeta变换的逆变换 // 作用:将 a[i] 从“倍数和”恢复为原值 // 即:a[i] = Σ_{d 是 i 的倍数} μ(d/i) * 原a[d] // 这里用减法实现逆变换 void mobius(LL a[]) { for (int i = n; i >= 1; i --) { // 从大到小,这样保证用到的都是还原后的 a 数组 for (int j = 2; j <= n / i; j ++) { a[i] = (a[i] - a[i * j] + P) % P; } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 1; i <= n; i ++) { cin >> a[i]; } for (int i = 1; i <= n; i ++) { cin >> b[i]; } // 第一步:对 a 和 b 分别做 zeta 变换 // 变换后:a[i] = Σ_{x 是 i 的倍数} a[x] // b[i] = Σ_{y 是 i 的倍数} b[y] zeta(a); zeta(b); // 第二步:逐点相乘 // 此时 c[i] = (Σ_{x 是 i 的倍数} a[x]) * (Σ_{y 是 i 的倍数} b[y]) // = Σ_{x,y 都是 i 的倍数} a[x] * b[y] for (int i = 1; i <= n; i ++) { c[i] = a[i] * b[i] % P; } // 第三步:对 c 做 Möbius 变换(逆 zeta) // 变换后:c[k] = Σ_{i 是 k 的倍数} μ(i/k) * 原c[i] // 而原c[i] = Σ_{x,y 都是 i 的倍数} a[x]*b[y] // 所以最终 c[k] = Σ_{x,y 满足 gcd(x,y)=k} a[x]*b[y] mobius(c); // 输出结果 for (int i = 1; i <= n; i ++) { cout << c[i] << " "; } cout << "\n"; return 0; }
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e6 + 10; const LL P = 998244353; LL a[N], b[N], c[N]; int n; // 约数zeta变换:对数组进行“约数和”变换 // 作用:将 a[d] 变为 sum_{i 是 d 的约数} a[i](即约数前缀和) // 等价于:a[d] = Σ_{i=1}^{d} [i|d] * a[i] // 这是lcm卷积中的“zeta变换”(约数方向) void zeta(LL a[]) { for (int i = n; i >= 1; i --) { // 从大到小遍历 i 的倍数,用到的都是原数组 for (int j = 2; j <= n / i; j ++) { a[i * j] = (a[i * j] + a[i]) % P; } } } // 约数Möbius变换:约数zeta变换的逆变换 // 作用:将 a[d] 从“约数和”恢复为原值 // 即:a[d] = Σ_{i 是 d 的约数} μ(d/i) * 原a[i] // 这里用减法实现逆变换 void mobius(LL a[]) { for (int i = 1; i <= n; i ++) { // 从小到大遍历i的倍数,用到的都是还原后的数组 for (int j = 2; j <= n / i; j ++) { a[i * j] = (a[i * j] - a[i] + P) % P; } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 1; i <= n; i ++) { cin >> a[i]; } for (int i = 1; i <= n; i ++) { cin >> b[i]; } // 第一步:对 a 和 b 分别做约数zeta变换 // 变换后:a[d] = Σ_{i 是 d 的约数} a[i] // b[d] = Σ_{j 是 d 的约数} b[j] zeta(a); zeta(b); // 第二步:逐点相乘 // 此时 c[d] = (Σ_{i 是 d 的约数} a[i]) * (Σ_{j 是 d 的约数} b[j]) // = Σ_{i,j 都是 d 的约数} a[i] * b[j] // = Σ_{i,j 满足 lcm(i,j) 是 d 的约数} a[i] * b[j] // 因为 i|d 且 j|d ⇔ lcm(i,j)|d for (int i = 1; i <= n; i ++) { c[i] = a[i] * b[i] % P; } // 第三步:对 c 做约数Möbius变换(逆约数zeta变换) // 变换后:c[k] = Σ_{d 是 k 的约数} μ(k/d) * 原c[d] // 而原c[d] = Σ_{i,j 满足 lcm(i,j)|d} a[i]*b[j] // 所以最终 c[k] = Σ_{i,j 满足 lcm(i,j)=k} a[i]*b[j] mobius(c); // 输出结果 for (int i = 1; i <= n; i ++) { cout << c[i] << " "; } cout << "\n"; return 0; }
- 1
信息
- ID
- 3230
- 时间
- 500ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 2
- 上传者