2021GDCPC广东省大学生程序设计竞赛 B.Byfibonacci

题意:
给一个数x,问有多少个 Fibonacci 子集其和为x
思路:
如果 n 的范围较小,那就是01背包,但 n<=1e7 就不能以值域作为状态,但我们可以发现看题解 :设 v1 , v2 为小于 xFibonacci 最大值和次大值,那么符合条件的子集有且仅有他们中的一个。

找到这个规律后就可以DP了,我们令

  • dp[0][i] 表示 i 选取最大的那个斐波那契数的乘积和
  • dp[1][i] 表示 i 选取次大的那个斐波那契数的乘积和

namo 转移方程则为

  • dp[0][i]=(dp[0][i-v1]+dp[1][i-v1])*v1
  • dp[1][i]=(dp[0][i-v2]+dp[1][i-v2])*v2; i-v2<v2
  • dp[1][i]=(dp[1][i-v2])*v2; i-v2>=v2

Code:

#include <bits/stdc++.h>
// #define debug freopen("_in.txt", "r", stdin);
#define debug freopen("_in.txt", "r", stdin), freopen("_out.txt", "w", stdout);
typedef long long ll;
typedef unsigned long long ull;
typedef struct Node *bintree;
using namespace std;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll maxn = 1e7 + 10;
const ll maxm = 2e6 + 10;
const ll mod = 998244353;
const double pi = acos(-1);
const double eps = 1e-8;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

ll T, n, m, q, d, kase = 0;
priority_queue<pair<ll, ll>> que;
ll arr[100];
ll dp[3][maxn];

void solve()
{
    scanf("%lld", &n);
    printf("%lld\n",(dp[0][n]+dp[1][n])%mod);
}

signed main()
{
    // debug;
    scanf("%lld", &T);
    // T = 1;
    arr[1] = arr[2] = 1;
    for (ll i = 2; i <= 50; i++)
    {
        arr[i] = arr[i - 1] + arr[i - 2];
    }
    dp[0][0] = 1;
    dp[0][1] = 1;
    dp[1][1] = 1;
    ll now = 0;
    for (ll i = 2; i <= 1e7; i++)
    {
        if (i >= arr[now])
        {
            while (i >= arr[now])
            {
                now++;
            }
            now--;
        }
        ll v1=arr[now],v2=arr[now-1];
        dp[0][i]=(dp[0][i-v1]+dp[1][i-v1])*v1;
        dp[0][i]%=mod;
        if(i-v2<v2)
        {
            dp[1][i]=(dp[0][i-v2]+dp[1][i-v2])*v2;
        }
        else
        {
            dp[1][i]=(dp[1][i-v2])*v2;
        }
        dp[1][i]%=mod;
    }
    while (T--)
    {
        solve();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值