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

    科技2022-07-14  103

    题目链接

    就是给我们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 有点长进了

    Processed: 0.016, SQL: 8