GDCPC B - Byfibonacci (dp,暴力)
阿新 • • 發佈:2021-07-05
題目
題解
方法一:
可以知道,最多35位Fibonacci數列就可以表示1e7的數。可以發現,前23位表示的數存在大量重複。因此可以先預處理出前23位的結果,然後剩下12位和預處理結果暴力卷積。前23位預處理最大的數為75024,剩下4096的需要處理,故最多計算75024*4096=2e8。不過一遍到不了那麼大,因此能過。
#include<iostream> using namespace std; const int N = 1e7 + 10; const int M = 998244353; typedef long long ll; int mx; int f[40]; int ans[N]; int ans2[N]; int s, mxv; void dfs(int p, int val, int res) { if(p >= mx) { mxv = max(mxv, val); ans[val] += res; ans[val] %= M; return ; } dfs(p + 1, val, res); dfs(p + 1, val + f[p], 1ll * res * f[p] % M); } void solve(int p, int val, int res) { if(val >= N) return ; if(p >= 35) { if(val) for(int i = 0; i <= mxv && val + i < N; i++) { ans2[val + i] += 1ll * ans[i] * res % M; ans2[val + i] %= M; } return ; } solve(p + 1, val, res); solve(p + 1, val + f[p], 1ll * res * f[p] % M); } int main() { f[0] = 1; f[1] = 1; for(int i = 2; i < 40; i++) { f[i] = f[i - 1] + f[i - 2]; } mx = 23; dfs(0, 0, 1); solve(mx, 0, 1); int t; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); printf("%d\n", (ans[n] + ans2[n]) % M); } }
方法二:
Fibonacci數列字首和為\(g(n)=f(n+2)-1\)。設小於等於n的最大的兩個項為\(f_{0n}\)和\(f_{1n}\),那麼n只能包含這兩者中的一個。因此可以設\(dp_{0n}\)和\(dp_{1n}\)為選擇兩者中一個的答案,這樣直接dp可以計算。注意選擇的衝突處理,詳見程式碼。
#include<iostream> using namespace std; const int N = 1e7 + 10; const int M = 998244353; typedef long long ll; int dp[2][N]; int f[40]; int main() { f[0] = f[1] = 1; dp[0][0] = 1; dp[0][1] = 1; dp[1][1] = 1; for(int i = 2; i < 40; i++) f[i] = f[i - 1] + f[i - 2]; int cur = 0; for(int i = 2; i < N; i++) { if(i >= f[cur]) { while(i >= f[cur]) cur++; cur--; } int v1 = f[cur], v2 = f[cur - 1]; dp[0][i] = 1ll * (dp[0][i - v1] + dp[1][i - v1]) * v1 % M; if(i - v2 < v2) // i-v2已經包含位置cur,故i不能選cur dp[1][i] = 1ll * (dp[0][i - v2] + dp[1][i - v2]) * v2 % M; else dp[1][i] = 1ll * (dp[1][i - v2]) * v2 % M; } int t; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); printf("%d\n", (dp[0][n] + dp[1][n]) % M); } }