题目链接

题意:

 中文的,而且说的很明白,但是要注意,这个数组中的数可以重复

思路:

  • 1、dp状态 dp[i,j]表示了长度为i并以j结尾的数组符合条件的方案数有多少种
  • 2、状态转移方程 dp[i,j] = sum(dp[i - 1][k] (k <= j) + dp[i - 1][x] (j % x != 0))

最暴力的dp方式是跑3个循环,这样时间复杂度是O( * )
由于时间是1000ms所以暴力dp肯定是过不了的
所以我们可以转化一下思路 采用 dp[i,j] = sum - dum 其中 sum = Sum(dp[i - 1][x]) (x >=1&& x <= k),dum = Sum(dp[i - 1][y] (y >= 1 && y <= k &&x % y != 0))
即采取了总和减去不符合条件的值即可得到符合条件的dp[i,j]
最后答案即为sum(dp[n,j]) (j >= 1&& j <= k)

ac 代码

 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 100000 + 5;
const int mod = 1e9 + 7;


ll pre[maxn],suf[maxn];
ll dp[12][maxn];
//状态,dp[i,j]表示了长度为i并以j结尾的数组符合条件的方案数有多少种

void Solve(int n,int k)
{
    for(int i = 1; i <= k; i++)  dp[1][i] = 1;//对于长度为1的数组,不管以什么数结尾,都是1种
    ll sum = 0,dum = 0;
    //如果三层循环暴力跑,一定会时间超限
    //所以采用求出上一层所有情况的数量 减去 其中是当前结尾数的倍数的数量
    //最后将其求和
    for(int i = 2; i <= n; i++){
        sum = 0;
        for(int j = 1; j <= k; j++) sum = (sum % mod + dp[i - 1][j] % mod) % mod;
        for(int j = 1; j <= k; j++){
            dum = 0;
            for(int q = j + j; q <= k; q += j) dum = (dum % mod + dp[i - 1][q] % mod) % mod;
            dp[i][j] = (sum - dum) % mod;
        }
    }
    ll res = 0;
    for(int i = 1; i <= k; i++) res = (res % mod + dp[n][i] % mod) % mod;
    cout<<res<<endl;
}

int main()
{
    int n,k;
    cin>>n>>k;
    Solve(n, k);
    return 0;
}