ZOJ 4011 Happy Sequence dp

ZOJ 4011 Happy Sequence dp


传送门: https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370222

题意

给 一 个 n 和 一 个 m , 表 示 你 可 以 重 复 用 [ 1 , n ] 里 的 数 字 。 给一个n和一个m,表示你可以重复用[1,n]里的数字。 nm[1,n]

构 造 一 个 数 列 , 数 列 中 前 一 个 数 字 必 须 是 后 一 个 数 字 的 因 子 , 即 a i ∣ a i + 1 。 构造一个数列,数列中前一个数字必须是后一个数字的因子,即a_i|a_{i+1}。 aiai+1

问 能 构 造 多 少 个 这 样 的 数 列 。 问能构造多少个这样的数列。

思路

比 如 n = 8 , m = 3. 可 以 构 造 : 比如n=8,m=3.可以构造: n=8m=3.

8    8    8 8\;8\;8 888
8    8    4 8\;8\;4 884
8    4    4 8\;4\;4 844
4    4    4 4\;4\;4 444
. . . ... ...

假 如 我 们 之 后 一 个 数 后 面 可 以 跟 多 少 因 子 , 然 后 这 个 因 子 后 面 . . . 假如我们之后一个数后面可以跟多少因子,然后这个因子后面... ...

这 就 是 个 递 推 啊 ! 这就是个递推啊! !

设 f [ i ] [ j ] 表 示 以 i 结 尾 , 长 度 为 j 的 序 列 个 数 。 设f[i][j]表示以i结尾,长度为j的序列个数。 f[i][j]ij

因 为 必 须 是 因 子 , 所 以 转 移 方 程 为 : 因为必须是因子,所以转移方程为:

f [ i ] [ j ] + = f [ k ] [ j − 1 ] ( k 为 i 的 因 子 ) f[i][j]+=f[k][j-1](k为i的因子) f[i][j]+=f[k][j1](ki)

所 以 要 先 预 处 理 所 有 数 字 的 因 子 , 然 后 进 行 d p 即 可 。 a n s = ∑ i = 1 n f [ i ] [ m ] 。 所以要先预处理所有数字的因子,然后进行dp即可。ans=\sum_{i=1}^nf[i][m]。 dpans=i=1nf[i][m]

Code

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
const ll mod = 1e9 + 7;

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

const int N = 2002;

ll f[N][N]; // 长度为j,以i结尾的个数
vector<int> p[N];

void init() {
    // p[1].emplace_back(1);
    for(int i = 1;i < N; i++) {
        p[i].emplace_back(1);
        for(int j = 2;j <= i; j++) {
            if(i % j == 0) p[i].emplace_back(j);
        }
    }
}

void solve() {
    init();
    for(int i = 1;i < N; i++) {
        f[i][1] = 1;
        for(auto k : p[i]) {
            for(int j = 2;j < N; j++) {
                f[i][j] = (f[i][j] + f[k][j - 1]) % mod;
            }
        }
    }
    int _; cin >> _;
    while(_--) {
        int n, m; cin >> n >> m;
        ll ans = 0;
        for(int i = 1;i <= n; i++) {
            ans = (ans + f[i][m]) % mod;
        }
        cout << ans << endl;
    }
}

signed main() {
    solve();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值