题目链接:传送门
青青草原上有 k k k只羊,他们聚集在包包大人的家里,举办一年一度的表彰大会,在这次的表彰大会中,包包大人让羊们按自己的贡献从小到大排成一排,以便于发放奖金。每只羊都会得到数值在 1~n 的奖金,并且第 i i i 只羊的奖金应为第 i + 1 i+1 i+1只羊的约数(即满足 a i ∣ a i + 1 a_i|a_{i+1} ai∣ai+1)。现在包包大人想知道一共有多少种不同的发放奖金的方式(两种发放奖金的方式不同是指在两种发放奖金的方式中存在某只羊拿到的奖金不同)
输入格式
一行两个正整数 n,k,满足
1
≤
n
,
k
≤
1000000
1\leq n,k\leq 1000000
1≤n,k≤1000000
输出格式
一行一个整数代表发放奖金的方案对 1000000007 取模的结果
首先我们先写出一个
O
(
k
n
l
o
g
n
)
O(knlogn)
O(knlogn)的大莉dp:
令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i只羊,且第
i
i
i只羊的奖金为
j
j
j的方案数。
显然的方程:
d
p
[
i
]
[
j
]
=
∑
k
∣
j
d
p
[
i
−
1
]
[
k
]
dp[i][j]=\sum_{k|j}dp[i-1][k]
dp[i][j]=∑k∣jdp[i−1][k]
也就是说,
d
p
[
i
]
=
d
p
[
i
−
1
]
∗
I
dp[i]=dp[i-1]*I
dp[i]=dp[i−1]∗I(
∗
*
∗表示狄利克雷卷积,
I
I
I表示常函数。狄利克雷卷积 & 莫比乌斯反演简介)
又因为
d
p
[
1
]
=
I
dp[1]=I
dp[1]=I,所以
d
p
[
k
]
=
I
k
dp[k]=I^k
dp[k]=Ik。
所以快速幂一下,
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)大莉求狄利克雷卷积,总复杂度
O
(
n
l
o
g
n
l
o
g
k
)
O(nlognlogk)
O(nlognlogk),不知道为什么能卡过去……跑得好像还挺快的qwq
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#define re register int
#define mod 1000000007
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(const int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=1000005;
const int INF=0x3f3f3f3f;
int n,k,Div[Size],I[Size],ans[Size],tmp[Size];
void mul(int *a,int *b,int *c) { //a卷积b
for(re i=1; i<=n; i++) c[i]=0;
for(re i=1; i<=n; i++) {
int maxj=Div[i];
for(re j=1; j<=maxj; j++) {
c[i*j]=(c[i*j]+(ll)a[i]*b[j])%mod;
}
}
}
int main() {
// freopen("T3.in","r",stdin);
// freopen("3.out","w",stdout);
n=read();
k=read()-1;
// n=k=1e6;
for(re i=1; i<=n; i++) {
I[i]=ans[i]=1;
Div[i]=n/i;
}
int siz=(n+2)<<2;
while(k) {
if(k&1) {
mul(ans,I,tmp);
memcpy(ans,tmp,siz);
}
mul(I,I,tmp);
memcpy(I,tmp,siz);
k>>=1;
}
int tot=0;
for(re i=1; i<=n; i++) {
tot=(tot+ans[i])%mod;
}
printf("%d",tot);
return 0;
}