dp数组含义:
dp[i][j]长度为i末尾元素为j的数组
状态转移方程
初始化
//本题的难点仍在于dp数组含义的设计 和 状态转移方程的列写
//对于序列中的每个元素ai,其值只与前一个元素有关,与前面其他元素无关
//且前一个元素与后一个元素成倍数关系,且不超过K
//故令dp[i][j]表示序列长度为i,且以j结尾的合法序列的数量
//设j的因子列表为ki,则dp[i][j]需要不断累加所有的dp[i-1][ki]
//如当前j=8,其因子有1,2,4,8
//则dp[i][8]=dp[i-1][1]+dp[i-1][2]+dp[i-1][4]+dp[i-1][8]
//若想当长度为i的时候末尾是8,则长度为i-1的时候末尾只能是1,2,4,8
//即长为i,以8结尾的序列数量等于长为i-1,以1,2,4,8结尾的序列的数量之和
//可以理解为对于长为i-1的序列,只有末尾为1,2,4,8时,再添加一个元素才有可能是8
//此外还要注意两个问题:
//一是初始化,当长度为1时不论选择1~K中的何值,都只有一种方案,故dp[1][1]~dp[1][K]均初始化为1
//二是在求j的因子时,为了提高效率只遍历到根号j,需要注意ki恰好为j的平方根时,不能重复累加
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll dp[2005][2005];//dp[i][j]表示序列长度为i,且以j结尾的合法序列的数量
int main()
{
int N,K;
ll ans=0;
cin>>K>>N;
for(int i=1;i<=K;i++)dp[1][i]=1;//只有1个数,无论是选择1~K中哪个数,都只有1种方案
for(int i=2;i<=N;i++)//从2~N遍历每一种序列长度
{
for(int j=1;j<=K;j++)//遍历当前序列中最后一个元素的每一种可能的取值j
{
for(int k=1;k*k<=j;k++)//遍历每一个可能是j的因子的数
//1 2 3 4 | 6
//36 18 12 9 | 6
{
if(k*k==j)//若k恰好是j的平方根,j/k==k,避免重复累加dp[i-1][k],与下文相区分
{
//长为i且以j结尾的序列个数 累加上 长为i-1且以k结尾的序列个数
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
else if(j%k==0)//否则如果k是j的因子且不是平方根,一次累加一对因子,效率更高
{
//长为i且以j结尾的序列个数 累加上 长为i-1且以j/k结尾的序列个数和长为i-1且以k结尾的序列个数
dp[i][j]=(dp[i][j]+dp[i-1][k]+dp[i-1][j/k])%mod;
}
}
}
}
for(int i=1;i<=K;i++)//累加所有长度为N的以1~K结尾的序列数量
{
ans=(ans+dp[N][i])%mod;
}
cout<<ans<<endl;
return 0;
}

419

被折叠的 条评论
为什么被折叠?



