习题加餐 9 1.可构造的序列总数

本文介绍了如何使用动态规划解决构造符合特定条件的序列问题,其中序列长度为n,元素不超过k,且满足Qi是aj-1的倍数。通过样例分析、算法设计和C++代码实现,详细阐述了解题思路和过程,并针对可能出现的整数溢出和数组边界问题进行了讨论。
摘要由CSDN通过智能技术生成

1.可构造的序列总数
问题描述
构造王国一年一度的构造大赛又开始了,这次构造王国的国王将只给定两个数字k和n,需要大家回答出能构造多少个符合以下条件的序列:
·序列的长度为n。
·1≤a₁≤a₂≤a₃≤……≤an≤k。
·Qi是aj-1的倍数(i≥2)。
由于答案可能非常大,你需要对10⁹+7取模。
输入格式
输入一行包括两个空格分隔的整数k和n。数据范围保证:1≤n,k≤2000。
输出格式
输出一个整数表示答案,答案需要对10⁹+7取模。
样例输入
22
样例输出
3
说明
样例中符合条件的序列有[1,1],[1,2],[2,2]总共三种。

我的答案:

一、信息

  • 输入:两个整数 kn,分别代表序列中元素的最大值和序列长度。
  • 输出:满足条件的序列数量,对 10^9 + 7 取模。

二、分析

信息作用

我们需要生成长度为 n 的序列,其中序列中的每个元素都不超过 k,且满足给定的条件。

思考和分析过程
  • 考虑使用动态规划求解问题。
  • 定义 dp[i][j] 为长度为 i 且最大元素为 j 的序列数量。
  • 由于 a_i 应该是 a_{i-1} 的倍数,我们需要统计所有可能的 a_{i-1} 使得 ja_{i-1} 的倍数。
题目样例分析
  • 输入:k=2, n=2
  • 输出:3,符合条件的序列有 [1,1], [1,2], 和 [2,2]

三、算法设计

  • 动态规划:
    • 初始化 dp[1][j] = 1 对于所有 1 ≤ j ≤ k,因为长度为 1 的序列只有自身。
    • 对于每个 i2n,遍历每个 j1k,然后计算 dp[i][j]
      • dp[i][j] 等于所有 dp[i-1][m] 的和,其中 mj 的因子。
    • 计算长度为 n 的序列总数为所有 dp[n][j] 的和。

四、代码实现(C++)

#include <iostream>
#include <vector>
using namespace std;

const int MOD = 1e9 + 7;

int main() {
    int k, n;
    cin >> k >> n;

    vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0));

    // 初始化长度为1的序列的数量
    for (int j = 1; j <= k; ++j) {
        dp[1][j] = 1;
    }

    // 填充 dp 表
    for (int i = 2; i <= n; ++i) {
        for (int j = 1; j <= k; ++j) {
            for (int m = j; m <= k; m += j) {
                dp[i][m] = (dp[i][m] + dp[i-1][j]) % MOD;
            }
        }
    }

    // 计算所有长度为 n 的序列的总数
    int total = 0;
    for (int j = 1; j <= k; ++j) {
        total = (total + dp[n][j]) % MOD;
    }

    cout << total << endl;
    return 0;
}

五、实现代码过程中可能遇到的问题

  • 整数溢出:需要在每次加法操作后对 10^9 + 7 取模。
  • 数组边界问题:要确保数组索引正确,不超过预定范围。

六、debug

  • 检查初始化是否正确。
  • 确保所有的模运算都已经正确地执行,以避免整数溢出。
  • 验证对于所有的 mjk 是否正确地按 j 的倍数递增。

正确答案:

源代码: 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
#define pb(s) push_back(s)
#define sz(s) ((int)s.size())
#define x first
#define y second
#define ms(s,x) memset(s, x, sizeof(s))
#define all(s) s.begin(),s.end()
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;


int n, k;
void solve()
{
    cin >> k >> n;
    std::vector<std::vector<LL>> f(n + 1, std::vector<LL>(k + 1));
    std::vector<std::vector<int>> e(k + 1);
    for (int i = 1; i <= k; ++i) {
        for (int j = i; j <= k; j += i) {
            e[j].push_back(i);
        }
    }
    for (int i = 1; i <= k; ++i) f[1][i] = 1;
    for (int i = 2; i <= n; ++i) {
        for (int j = 1; j <= k; ++j) {
            for (auto v : e[j]) {
                f[i][j] = (f[i][j] + f[i - 1][v]) % mod;
            }
        }
    }
    LL ans = 0;
    for (int i = 1; i <= k; ++i) ans = (ans + f[n][i]) % mod;
    cout << ans << '\n';
}
int main()
{
    ios_base :: sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << setiosflags(ios::fixed) << setprecision(2);
    int t = 1;
    while (t--)
    {
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值