题目链接 http://codeforces.com/problemset/problem/414/B
题意,求长度为k的数组个数,该数组的元素都是正正数,且不超过n,且后一项能整除前一项。
给出n,k,求这样的数组有多少个。 (n,k<=2000)
思路,
这道题可以用动态规划的思想来完成。
我们设数组 dp[k][n] 。
那么dp[i][j]表示长度为i的,每个数不超过n的数组个数。
那么有 dp[i][j] = sum(dp[i-1][k]) 其中 k属于 j整除k等于0。
时间复杂度本来为O(n*n*k)
但是遍历整除的时候,可以只进行到sqrt(n)
所以可以优化成O(n*sqrt(n)*k)
一直过不去,原来我每次计算的时候,都对数取了一次模。所以超时了。
因为取模是特别浪费时间的。所以只要到两数相加的时候,再取模就好了。
以下为代码
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long LL;
typedef long long ll;
const int maxn = 2002;
const int modn = 1e9+7;
const int INF = 0x3f3f3f3f;
char str[maxn];
int a[maxn];
ll dp[maxn][maxn];
void show(int a[],int n){
for(int i=0;i<n;i++){
printf("%d ",a[i]);
}printf("\n");
}
int main(){
// freopen("C:\\Users\\lenovo\\Desktop\\data.in","r",stdin);
int n,kk;
while(scanf("%d%d",&n,&kk)+1){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
dp[i][1] = 1;
}
for(int i=1;i<=n;i++){
for(int j=2;j<=kk;j++){
for(int k=1;k*k<=i;k++){
if(k*k==i){
dp[i][j]+= dp[k][j-1];
dp[i][j]%=modn;
}else if(i%k==0){
dp[i][j] += dp[k][j-1]+dp[i/k][j-1];
dp[i][j]%=modn;
}
}
}
}
int ans = 0;
for(int i=1;i<=n;i++){
ans += dp[i][kk];
ans %= modn;
}
printf("%d\n",ans);
}
}