这个题非常好,尤其是其正解的思路转换方式,真的特别需要整理一下。
首先,k个羊,n种钱,构造序列为前一个是后一个数的因子,不难想到记录长度,因为长度为i+1的可以由长度为i的转移过去,朴素可以想到dp【i】【j】代表长度为i,末尾为j的方案数,那么转移就是f[i+1][j]=d|j dp[i][d] 新加入的这个数是j的因子。这样时间复杂度根本不行,所以我们大胆的将一个数拆为log级别,怎么想呢,末尾数必须要记录,将一维定为长度已经为k,k里面不同的个数有多少个,因为每次都是倍数关系,这个数量级就是log的了,所以dp【i】【j】代表k个羊中的方案数,最后咱们将同种的所有dp数求和,在k个羊中有i中不同的方式,就是隔板法cnm了,而且状态转移也很明显,dp【i+1】k从1到n/j【j*k】+=dp[i][j],也就是咱们最开始的那种思路的一种转换
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxx=1e6+5;
ll n,i,j,k,t,cnt=0;
const ll mod=1e9+7;
ll dp[23][maxx]={0};
ll sum[maxx]={0};
ll jiecheng[maxx];
ll niyuan(ll a,ll b) // 阶乘逆元求解。
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
ll c(ll a,ll b)
{
ll x=niyuan(jiecheng[b],mod-2)*niyuan(jiecheng[a-b],mod-2)%mod;
return (jiecheng[a]*x)%mod;
}
int main()
{
ll l,r,p;
jiecheng[0]=1;
jiecheng[1]=1;
for(i=2;i<maxx;i++) jiecheng[i]=(i*jiecheng[i-1])%mod;
cin>>n>>k;
//cnt=log2(n)+1;
//printf("%lld\n",cnt);
//printf("%lld\n",jiecheng[6]);
for(i=1;i<=n;i++) dp[1][i]=1; // 不同种为1的全是一种
for(i=1;i<21;i++) // log2(n)的最大值
{
for(j=1;j<=n;j++)
{
for(p=j+j;p<=n;p+=j)
{
dp[i+1][p]+=dp[i][j];// 状态转移
dp[i+1][p]%=mod;
}
}
}
for(i=1;i<21;i++)
for(j=1;j<=n;j++)
{
sum[i]+=dp[i][j]; sum[i]%=mod; // 将不同种数的和加起来。
}
ll ans=0;
for(i=1;i<=n&&i<21;i++)
{
ans=(ans+sum[i]*c(k-1,i-1))%mod; // 在k个中不同的i种的方案数的求和。
}
//for(i=1;i<=n;i++) printf("%lld\n",sum[i]);
printf("%lld\n",ans);
return 0;
}
青青草原上有k只羊,他们聚集在包包大人的家里,举办一年一度的表彰大会,在这次的表彰大会中,包包大人让羊们按自己的贡献从小到大排成一排,以便于发放奖金。每只羊都会得到数值在1~n的奖金,并且第i只羊的奖金应为第i+1只羊的约数(即满足ai|ai+1)。现在包包大人想知道一共有多少种不同的发放奖金的方式(两种发放奖金的方式不同是指在两种发放奖金的方式中存在
某只羊拿到的奖金不同)
输入
一行两个正整数n,k,满足(1<=n,k<=1000000)
输出
一行一个整数代表发放奖金的方案对1000000007取模的结果
样例输入 Copy
6 4
样例输出 Copy
39