Luogu P4133 [BJOI2012] 最多的方案 题解
记忆化搜索,设
- 若
,则 ; - 若
,则 ; - 若
,则 ; - 若
,则 ,因为此时不分解出 就一定无解; - 否则,
。
这个做法很轻松地通过了所有数据,我们如何知道它的时间复杂度?
考虑势能分析,设
断言:要么
证明
边界情况令
,接下来假设 。 考虑在转移过程中说明断言成立。
- 若
,则 满足断言; - 否则,分类
中的两项。注意此时 ,所以
- 对于
,有 ,满足断言。进一步,其继续迭代一次仍满足断言; - 对于
,下一次迭代必然走向 ,此时 满足断言。 断言成立。
Q.E.D.
推论
设
证明
足够大时,斐波那契数列相邻两项比值 。 继续上一个证明的分类讨论:
- 第一类情况中,一次迭代使得势能变为原来的
,两次即为 ; - 第二类情况中,两次迭代使得势能变为原来的
,两种子情况分别贡献 ; - 特殊地,如果第一类情况后接着第二类情况,三次为
,平均一次 。 因此势能至少变为
。 Q.E.D.
所以,总迭代次数约为
注意
#include <iostream> #include <map> using namespace std; typedef long long i64; const i64 lim = 1e18; int tail; i64 fib[100]; i64 f[100]; map<pair<i64, int>, int> mp; static inline i64 dfs(i64 x, int k) { if (!x) return 1; if (k == 1) return 0; if (mp.count({x, k})) return mp[{x, k}]; if (x < fib[k]) return mp[{x, k}] = dfs(x, k - 1); if (f[k - 1] < x) return mp[{x, k}] = dfs(x - fib[k], k - 1); return mp[{x, k}] = dfs(x, k - 1) + dfs(x - fib[k], k - 1); } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); fib[1] = fib[2] = 1; for (tail = 2; fib[tail] <= lim; ++tail) fib[tail + 1] = fib[tail] + fib[tail - 1]; for (int i = 1; i <= tail; ++i) f[i] = f[i - 1] + fib[i]; i64 n; cin >> n; cout << dfs(n, tail) << '\n'; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现