由于博主对DP的感知能力过于低下,考试的时候竟然没发现这是DP。。。
发现每个数一定是它前面一个数的倍数,所以这个数列一定是有序的,也不难发现这个数列最多有(O(logn))个不同的数,所以设(f[i][j])表示有(i)个不同的数,最后一个数是(j)的方案数。
所以最后有(d)个互不相同的数的个数(cnt[d]=sum f[d][j]),然后我们考虑把这些数放到长度为(n)的题目要求的数列中,由组合数中隔板原理,可以知道有(d)种数的方案有(cnt[d] imes inom{k-1}{d-1}),所以(ans=sum_{d=1}^{log_n} cnt[d] imes inom{k-1}{d-1})。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1000009,M=1000000007;
int n,k,a[N],ans,f[25][N],fac[N],invf[N];
void init()
{
scanf("%d %d",&n,&k);
}
void ADD(int &x,int y)
{
x=(x+y)%M;
}
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=1LL*res*a%M;
b>>=1,a=1LL*a*a%M;
}
return res;
}
void work()
{
fac[0]=1;
int Q=N-9;
for (int i=1;i<=Q;i++)
fac[i]=1LL*fac[i-1]*i%M;
invf[Q]=ksm(fac[Q],M-2);
for (int i=Q-1;i>=0;i--)
invf[i]=1LL*invf[i+1]*(i+1)%M;
for (int i=1;i<=n;i++)
f[1][i]=1;
for (int i=1;1<<i-1<=n;i++)
for (int j=1;j<=n>>1;j++)
if(f[i][j])
for (int k=j+j;k<=n;k+=j)
ADD(f[i+1][k],f[i][j]);
int ans=0;
for (int i=1;1<<i-1<=n;i++)
{
int tmp=0;
for (int j=1;j<=n;j++)
ADD(tmp,f[i][j]);
ADD(ans,(int)(1LL*tmp*fac[k-1]%M*invf[i-1]%M*invf[k-i]%M));
}
printf("%d
",ans);
}
int main()
{
init();
work();
return 0;
}