GDCPC2021 B - Byfibonacci (dp,暴力)
题目
题解
方法一:
可以知道,最多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);
}
}