Codeforces 126D Fibonacci Sums dp计数&&思维

题目链接

就是给我们t组数据
每次询问一个值n 范围为1e18
问用不同的斐波那契数 求和组成这个数的方案数
emmm 一开始想的是斐波那契数不过就80来个就达到了1e18
想了想01背包计数 显然挂掉了
我们推了几个数发现,似乎所有的数都可以由唯一的几个斐波那契数求和组成(确实如此)
因为不能有相同的fib数 所以fib1=1 fib2=2
那么问题就在于 我们对于每个斐波那契数 两种状态,一种是把他拆了 ,另一种是不管它 ,拆的话 因为fib (i)=fib(i-1)+fib_(i-2) 那么多出来的数我们又要考虑拆不拆的问题 有点像二进制了 我们继续推下去
观察几个数 00001 (fib_5) =00110 =11010 方案数1+2=3
自己多画几个数观察吧,从左往右代表 取1的位置代表我们取了fib(x)
那么我们观察到把当前位置拆了的方案数等于 前一个fib数的下标差乘上一个状态的方案数 ,显然上一个状态是两种, 拆与不拆 , 那么就变成了一个乘法原理+算贡献的dp 计数

此时dp方程已经大概有了
定义dp[1][i] 第i 个斐波那契数 不拆 (这里的i 指的是组成询问数n的)
dp[0][i] 把它拆了
那么我们的初始状态 dp[1][1]=1 dp[0][1]=(pos[1]-1) / 2
pos记录组成n的 fib的位置 (从小到大)
dp[1][i]=dp[0][i-1]+dp[1][i-1];
dp[0][i]=(vis[i]-vis[i-1]-1)/2dp[1][i-1]+(vis[i]-vis[i-1])/2dp[0][i-1];
那么答案就是最后一个fib拆和不拆的总方案数和 取模

 #include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
 using namespace std;
 ll gcd(ll a,ll b){if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){ll ans=1;if(b==0){return ans%mod;}while(b){if(b%2==1){b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){ll ans = 1;a%=p;while(b){if(b&1){ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元   (分子*qp(分母,mod-2,mod))%mod;
ll fib[102];
ll vis[500];
ll dp[3][500];
signed main(){
fib[1]=1;
fib[2]=2;
ll op=3;
for(int i=3;;i++,op++){
  fib[i]=fib[i-1]+fib[i-2];
  if(fib[i]>=1e18||fib[i]<fib[i-1]){
    break;
  }
}
ll t;
read(t);
while(t--){
ll n;
read(n);
ll le=0;
for(int i=op;i>=1;i--){
if(n>=fib[i]){
    n=n-fib[i];
    vis[++le]=i; //fib下标
}

}
reverse(vis+1,vis+1+le);
dp[1][1]=1;  //第一个数  不拆
dp[0][1]=(vis[1]-1)/2;    //拆
for(int i=2;i<=le;i++){
dp[1][i]=dp[0][i-1]+dp[1][i-1];
dp[0][i]=(vis[i]-vis[i-1]-1)/2*dp[1][i-1]+(vis[i]-vis[i-1])/2*dp[0][i-1];

}
printf("%lld\n",dp[0][le]+dp[1][le]);


}


}

通宵刷了几天dp 有点长进了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值