GDCPC2021 B - Byfibonacci (dp,暴力)

题目

source

题解

方法一:
可以知道,最多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);
	}
}
posted @ 2021-07-05 17:52  limil  阅读(221)  评论(0编辑  收藏  举报