把『n 左边加一个不超过 n/2 的数』的生成过程建成递推 f(n)=1+Σf(1..n/2),相邻前缀和降为一维线性递推,预处理后 O(1) 回答。
OJ: roj
题目 ID: 3504
难度:入门
标签:入门动态规划递推前缀和python
创建: 2026-10-02 03:53
更新: 2026-10-06 12:16
题目描述
给定自然数
输入:一个自然数 6,输出 6。
思路
设 int 范围,需用 long long。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 03:50
# update_at: 2026-10-02 03:50
import sys
N = 1000 # 题面 n 的上界
def build_counts(limit: int) -> list[int]:
"""count[n] = n 经上述规则能生成的全部数的个数(含 n 自己)。
直接按定义转移是 O(n^2) 的;注意 f(1..n/2) 恰好是 count 的连续前缀,
所以用前缀和 sum_pre[m] = f(1)+...+f(m) 把转移降为 O(1)。
"""
count = [0] * (limit + 1) # f(n):n 自己算一个生成的数
sum_pre = [0] * (limit + 1) # sum_pre[m] = f(1) + f(2) + ... + f(m)
for n in range(1, limit + 1):
# n 生成的数 = {n} ∪ {拼接(g, n) : g ∈ m 生成的数, 1<=m<=n/2};位数可反解,单射不重
count[n] = 1 + sum_pre[n // 2]
sum_pre[n] = sum_pre[n - 1] + count[n]
return count
count = build_counts(N)
def solve() -> None:
n = int(sys.stdin.read())
print(count[n])
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 12:16
* update_at: 2026-10-06 12:16
*/
#include <iostream>
using namespace std;
typedef long long ll;
const int MAXN = 1005;
ll f[MAXN]; // f[i] 表示从 i 出发(含 i 本身)能得到的数的个数
ll s[MAXN]; // s[i] = f[1] + f[2] + ... + f[i],即 f 的前缀和
int main() {
int n;
cin >> n;
// 递推:f[i] = 1 + (1..i/2 每个数左边拼上 i 之后各自的产物数)
// 即 f[i] = 1 + s[i/2],再用前缀和把转移降到 O(1)
f[0] = 0;
s[0] = 0;
for (int i = 1; i <= n; i++) {
f[i] = 1 + s[i / 2];
s[i] = s[i - 1] + f[i];
}
cout << f[n] << endl;
return 0;
}