P r o b l e m \mathrm{Problem} Problem
S o l u t i o n \mathrm{Solution} Solution
显然这道题目和两个函数没有任何关系,仅仅是为了考察你是否会线性筛法以及用于权值的计算。
我们设
f
k
(
n
)
f_k(n)
fk(n)表示k个数乘积为n的权值和。那么就有:
f
k
(
n
)
=
∑
k
∣
n
f
p
(
k
)
×
f
k
−
p
(
n
k
)
f_k(n)=\sum_{k|n} f_{p}(k)\times f_{k-p}(\frac{n}{k})
fk(n)=k∣n∑fp(k)×fk−p(kn)
然后观察一下这一个算式的特性,我们发现其实
p
p
p在这里可以是任意的一个数。
那么我们将这里的一次操作记为一次卷积。那么我们发现在这里其实就是做k次卷积。
因此我们需要用到狄利克雷卷积算法。由于该卷积支持快速幂,我们可以使用卷积快速幂来实现。
具体做法如下:
- 根据权值记初始序列为 f f f,另 g 1 = 1 g_1=1 g1=1.在这里, f f f相当于快速幂内的 a a a, k k k相当于 b b b,而在 g g g数组中统计答案。那么我们再做 k k k次快速幂以后,我们只需要输出 g [ 1... n ] g[1...n] g[1...n]即可。
- 当 k k k是奇数时,我们对 f f f和 g g g同时进行一次卷积。
- 每一次都对 f f f自身完成一次卷积。
我们可以用倍数法完成卷积,时间复杂度 O ( n l o g n ) O(n\ \mathrm{log}\ n) O(n log n).
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 2e5;
const int P = 998244353;
int cnt = 0, n, k;
int phi[N], mul[N], vis[N], prime[N], f[N], g[N], res[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void init(void)
{
phi[1] = 1, mul[1] = 1;
for (int i=2;i<=1e5;++i)
{
if (vis[i] == 0) mul[i] = -1, phi[i] = i-1, prime[++cnt] = i;
for (int j=1;j<=cnt&&i*prime[j]<=1e5;++j)
{
vis[i*prime[j]] = 1;
if (i % prime[j] == 0)
{
mul[i*prime[j]] = 0;
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
mul[i*prime[j]] = -mul[i];
phi[i*prime[j]] = phi[i]*(prime[j]-1);
}
}
g[1] = 1;
for (int i=1;i<=n;++i) f[i] = 2 * phi[i] + mul[i];
return;
}
void Mul(void)
{
memset(res,0,sizeof res);
for (int i=1;i<=n;++i)
for (int j=1;i*j<=n;j++)
res[i*j] = 1LL * (res[i*j] + 1LL * f[i] * g[j]) % P;
for (int i=1;i<=n;++i)
g[i] = res[i];
return;
}
void mulself(void)
{
memset(res,0,sizeof res);
for (int i=1;i<=n;++i)
for (int j=1;i*j<=n;++j)
res[i*j] = 1LL * (res[i*j] + 1LL * f[i] * f[j]) % P;
for (int i=1;i<=n;++i)
f[i] = res[i];
return;
}
void power(int k)
{
while (k > 0)
{
if (k & 1) Mul();
mulself(), k >>= 1;
}
for (int i=1;i<=n;++i)
return;
}
int main(void)
{
n = read(), k = read();
init();
power(k);
for (int i=1;i<=n;++i) printf("%d ", g[i]);
return 0;
}