一道狄利克雷卷积模板题的组合做法
题目描述:
给定 f(x) 在 [1,n] 上的取值,求函数:
在 [1,n] 上的取值。
狄利克雷卷积做法
容易发现, g=1k×f ,直接用狄利克雷卷积快速幂即可。
组合做法
显然,
p
的所有因子在整除关系
考虑求从
p
开始长度为
然而这个dp的状态规模是
O(n2)
的,考虑原因:存在很长的链是因为可以多次在一个数上重复(即在自环上走)。设
H[p][k]
为长度为从
p
开始长度为
显然
k≤lgK
,现在向路径上插入自环。考虑
H[n][p]
对
h[n][K]
的贡献,
p≤K
。需要在
p
个位置上插入
而答案
g(n)
等于
n
的所有因子的
自此问题得以解决。复杂度 O(nlg2n) 。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct node {
int to, next;
} edge[MAXN*20];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }
int n, k;
void init()
{
for (register int i = 1; i <= 100000; i++)
for (register int j = 2; j*i <= 100000; j++)
push(j*i, i);
}
long long f[MAXN][20], a[MAXN];
const long long mod = 1000000007ll;
long long fac[MAXN], inv[MAXN], g[MAXN], g2[MAXN];
long long power(int a, long long n)
{
if (n == 0) return 1;
long long b = a, ans = 1;
for (int k = 0; k <= 30; k++) {
if (n&(1ll<<k)) (ans *= b) %= mod;
(b *= b) %= mod;
}
return ans;
}
inline long long choose(int K, int k)
{ return fac[K]*inv[k]%mod*inv[K-k]%mod; }
void init_c()
{
inv[0] = 1;
for (int i = 1; i <= 100000; i++) inv[i] = (inv[i-1]*power(i, mod-2))%mod;
fac[0] = 1;
for (int i = 1; i <= 100000; i++) (fac[i] = fac[i-1]*i) %= mod;
}
void dp()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%lld", &f[i][1]);
for (int j = 2; j <= 17; j++)
for (register int i = 1; i <= n; i++) {
f[i][j] = 0;
for (register int k = head[i]; k; k = edge[k].next) {
int to = edge[k].to;
(f[i][j] += f[to][j-1]) %= mod;
}
}
for (register int i = 1; i <= n; i++) {
long long ans = 0;
for (register int j = 1; j <= 17; j++)
(ans += f[i][j]*choose(k-1, j-1)%mod) %= mod;
g[i] = ans;
}// cerr << endl;
memset(g2, 0, sizeof g2);
for (register int i = 1; i <= n; i++)
for (register int j = 1; j*i <= n; j++)
(g2[j*i] += g[i]) %= mod;
for (int i = 1; i <= n; i++)
printf("%lld ", g2[i]);
puts("");
}
int main()
{
freopen("b.in", "r" , stdin);
freopen("b.out", "w", stdout);
init();
init_c();
int T; scanf("%d", &T);
while (T--) dp();
return 0;
}