CF 126D - Fibonacci Sums 题解

766 字
4 分钟
CF 126D - Fibonacci Sums 题解
Done

您已获得最佳的阅读体验!

题目地址:CF 126D

题目难度:省选/NOI-

计算一个整数被分解成若干个各不相等的Fibonacci数列中的数的方案。

前置 齐肯多夫定理#

齐肯多夫定理的内容是:

任何正整数都可以被表示成若干不连续的斐波那契数之和( 除外)

而对于一个正整数,我们可以按照二进制分解的策略:先贪心地找到一个最大的 满足 ,然后用 减去 ,以此类推直到减为

斐波那契数列一般定义为:,这里我们做一个整体左移,本文所述的斐波那契数列应是数列 ,也就是

类比二进制,齐肯多夫分解是可以用一个 串表示出来的。例如:。因此它的齐肯多夫拆分就是:

不难发现,按照贪心策略计算出的拆分是不会存在相邻的两个 的。因为一旦出现连续的 ,都可以遵循斐波那契数的定义把它变为一个更大的斐波那契数。

回归正题#

假如我们现在已经得到了一个齐肯多夫拆分,如何计算题目要求的方案总数呢?

可以利用“标准齐肯多夫拆分中不存在相邻 ”的结论。对于原串的一个模式串 *100*(星号代表任意 串),我们可以拆成 *011*。如果在末尾的 后还有更多的 ,那其实也是可以再次拆分的。

注意到要求方案数,考虑动态规划。我们用 表示拆分中第 的出现位置,并令状态转移方程为 ,表示选择是否删除 位后的总方案数。

接下来考虑当前状态从何而来。如果选择不删当前这一位,答案只能从前一位 那里贡献而来,也即

如果选择删除这一位,对于接下来的操作,我们仍然可以选择删除或不删除。发现删到最后整个字符串一定是像 101010101 这样交错排列的。于是可以得到:

#include <bits/stdc++.h>
#define N 90
using namespace std;
typedef long long ll;
vector<int> pos;
ll fib[N];
ll dp[N][2];
void init() {
fib[1] = fib[2] = 1;
for (int i = 3; i < N; i++) fib[i] = fib[i - 1] + fib[i - 2];
}
int solve(ll x) {
pos.clear();
for (int i = N - 1; i >= 1; i--) {
if (!x) break;
if (fib[i] <= x) {
x -= fib[i];
pos.push_back(i - 1);
}
}
reverse(pos.begin(), pos.end());
return pos.size();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
init();
while (t--) {
memset(dp, 0, sizeof dp);
ll x;
cin >> x;
int len = solve(x);
dp[0][0] = 1;
dp[0][1] = (pos[0] - 1) / 2;
for (int i = 1; i < len; i++) {
dp[i][0] = dp[i - 1][0] + dp[i - 1][1];
dp[i][1] = dp[i - 1][0] * ((pos[i] - pos[i - 1] - 1) / 2) + dp[i - 1][1] * ((pos[i] - pos[i - 1]) / 2);
}
cout << dp[len - 1][1] + dp[len - 1][0] << endl;
}
return 0;
}

CF 通过记录

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

CF 126D - Fibonacci Sums 题解
https://justpureh2o.cn/articles/126/
作者
JustPureH2O
发布于
2024-10-31
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
JustPureH2O
穷方圆平直之情,尽规矩准绳之用
公告
JustPureH2O 的博客现已正式迁移至 Astro!原 Hexo 网站将移至 https://hexo.justpureh2o.cn/
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
101
分类
13
标签
56
总字数
374,694
运行时长
0
最后活动
0 天前

目录