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 有点长进了

猜你喜欢

转载自blog.csdn.net/weixin_45948940/article/details/108918261
今日推荐