链接:https://ac.nowcoder.com/acm/problem/21738
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
牛牛喜欢这样的数组:
1:长度为n
2:每一个数都在1到k之间
3:对于任意连续的两个数A,B,A<=B 与(A % B != 0) 两个条件至少成立一个
请问一共有多少满足条件的数组,对1e9+7取模
输入描述:
输入两个整数n,k 1 ≤ n ≤ 10 1 ≤ k ≤ 100000
输出描述:
输出一个整数
示例1
输入
复制
2 2
输出
复制
3
示例2
输入
复制
9 1
输出
复制
1
示例3
输入
复制
3 3
输出
复制
15
示例4
输入
复制
2 1234
输出
复制
1515011
思路:
一共n位对每一位上的数值从1-k枚举即可,状态转移方程如下
dp[i][j] = dp[i-1][p] {p<=j || p%j != 0}
三次循环肯定爆炸,所以代码加入了一些预处理,枚举j的过程中记录下j之前的那些方案数,然后在暴力的找出那些j的倍数的方案数,这个就可以预处理了啊!
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6+50;
const int mod = 1e9+7;
typedef long long LL;
int dp[11][MAX_N],n,k;
vector<int> a[MAX_N];
int main(void)
{
cin>>n>>k;
for(int i=1; i<=k; i++) //倍数预处理
for(int j=2; i*j<=k; j++)
a[i].push_back(i*j);
memset(dp,0,sizeof(dp)); //边界初始化
for(int i=1; i<=k; i++) dp[1][i] = 1;
for(int i=2; i<=n; i++) //位数枚举
{
LL ans = 0,sum=0,temp; //注意这里要long long,防爆
for(int j=1; j<=k; j++) ans = (ans+dp[i-1][j])%mod; //前面的方案数总和
for(int j=1; j<=k; j++)
{
temp = ans;
dp[i][j] = (sum+dp[i-1][j])%mod;
sum = (sum+dp[i-1][j])%mod; //状态的累计
temp = (temp+mod-sum)%mod;
for(int q=0; q<a[j].size(); q++)
temp = (temp+mod-dp[i-1][a[j][q]])%mod; //枚举倍数减去
dp[i][j] = (dp[i][j]+temp)%mod;
}
}
LL res = 0;
for(int i=1; i<=k; i++) res = (LL)(res+dp[n][i])%mod;
cout<<res;
return 0;
}