1. 程式人生 > 其它 >GDCPC B - Byfibonacci (dp,暴力)

GDCPC 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);
	}
}