斐波那契求和(矩陣快速冪+多項式拆分)

https://ac.nowcoder.com/acm/contest/5477/J

Fib(i)表示斐波那契函數,Fib(n)=Fib(n-1)+Fib(n-2),如Fib(1)=1,Fib(2)=1,Fib(3)=2,Fib(4)=3,Fib(5)=5,Fib(6)=8。

給定正整數n和k,求:

由於結果太大,你需要把求和的結果對998244353取餘

介紹兩種矩陣構造方法:

方法一:

\bg_black F(n) = \sum_{i=1}^{n}i^{k}Fib(i),那麼F(n + 1) = F(n) + (n+1)^{k}Fib(n+1)

                                                                      = F(n) + (n+1)^{k}(Fib(n) + Fib(n-1))

                                                                      =F(n) + \sum_{i=0}^{k}\binom{k}{i}n^{k-i}Fib(n) + \sum_{i=0}^{k}\binom{k}{i}n^{k-i}Fib(n-1)

構造一個一個矩陣維護F(n), n^{i}Fib(n), n^iFib(n-1) (0<=i<=k) 一共2(k+1)+1項

代碼:

/**/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <stack>
#include <queue>

typedef long long LL;
using namespace std;

const long long mod = 998244353;

int k, M;
LL n, c[105][105], a[205][205], p[105];

void init(int k){
	c[0][0] = 1;
	for (int i = 1; i <= k; i++){
		c[i][0] = 1;
		for (int j = 1; j <= i; j++){
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
		}
	}
	p[0] = 1;
	for (int i = 1; i <= k; i++) p[i] = (p[i - 1] << 1) % mod;
}

LL poww(LL x, LL num){
	LL res = 1;
	while(num){
		if(num & 1) res = res * x % mod;
		x = x * x % mod;
		num >>= 1;
	}
	return res;
}

void mul(LL a[205][205], LL b[205][205]){
	LL c[M + 1][M + 1];
	memset(c, 0, sizeof(c));
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			if(a[i][j] == 0) continue;
			for (int k = 0; k <= M; k++){
				c[i][k] = (c[i][k] + a[i][j] * b[j][k] % mod) % mod;
			}
		}
	}
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			a[i][j] = c[i][j];
		}
	}
}

LL solve(LL num){
	if(num == 1) return 1;
	if(num == 2) return (p[k] + 1) % mod;
	num -= 2;
	a[0][0] = 1;
	for (int i = 1; i <= k + 1; i++) a[0][i] = c[k][i - 1], a[0][i + k + 1] = c[k][i - 1];
	for (int i = 1; i <= k + 1; i++){
		for (int j = i; j <= k + 1; j++){
			a[i][j] = c[k - i + 1][j - i];
			a[i][j + k + 1] = c[k - i + 1][j - i];
		}
	}
	for (int i = k + 2; i <= M; i++){
		for (int j = 1; j <= k + 1; j++){
			a[i][j + i - k - 2] = c[k - i + k + 2][j - 1];
		}
	}
	// for (int i = 0; i <= M; i++){
	// 	for (int j = 0; j <= M; j++){
	// 		printf("%lld ", a[i][j]);
	// 	}
	// 	printf("\n");
	// }
	LL res[205][205];
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			if(i == j) res[i][j] = 1;
			else res[i][j] = 0;
		}
	}
	while(num){
		if(num & 1) mul(res, a);
		mul(a, a);
		num >>= 1;
	}
	LL ans = (p[k] + 1) * res[0][0] % mod;
	for (int i = 1; i <= k + 1; i++) ans = (ans + p[k + 1 - i] * res[0][i] % mod) % mod;
	for (int i = 1; i <= k + 1; i++) ans = (ans + p[k + 1 - i] * res[0][i + k + 1] % mod) % mod;
	return ans;
}

int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);

	scanf("%lld %d", &n, &k);
	init(k);
	M = (k + 1) * 2;
	printf("%lld\n", solve(n));

	return 0;
}
/**/

 

 

方法二:

G_{k}(n)=\sum_{i=1}^{n}(n-i)^{k}Fib(i),F_{k}(n)=\sum_{i=1}^{n}i^{k}Fib(i)

G_{k}(n)=\sum_{i=1}^{n}(n-i)^{k}Fib(i)

           =\sum_{i=1}^{n}(\sum_{j=0}^{k} \binom{k}{j}n^{k-j}(-i)^{j})Fib(i)

          =\sum_{j=0}^{k}\binom{k}{j}n^{k-j}(-1)^{j}\sum_{i=1}^{n}i^{j}Fib(i)

         =\sum_{j=0}^{k}\binom{k}{j}n^{k-j}(-1)^{j}F_{j}(n)

          =\sum_{j=0}^{k-1}\binom{k}{j}n^{k-j}(-1)^{j}F_{j}(n)+(-1)^{k}*F_{k}(n)

(-1)^{k}*F_{k}(n)=G_{k}(n)-\sum_{j=0}^{k-1}\binom{k}{j}n^{k-j}(-1)^{j}F_{j}(n)

則有

G_{k}(n+1)-G_{k}(n)=\sum_{i=1}^{n+1}(n+1-i)^{k}Fib(i) - \sum_{i=1}^{n}(n-i)^{k}Fib(i) 

                                 =\sum_{i=1}^{n}((n-i+1)^{k}-(n-i)^{k})Fib(i)

                                 =\sum_{i=1}^{n}(\sum_{j=0}^{k}\binom{k}{j}(n-i)^{j}-(n-i)^{k})Fib(i)

                                =\sum_{i=1}^{n}\sum_{j=0}^{k-1}\binom{k}{j}(n-i)^{j}Fib(i)

                                =\sum_{j=0}^{k-1}\binom{k}{j}\sum_{i=1}^{n}(n-i)^{j}Fib(i)

                               =\sum_{k-1}^{i=0}\binom{k}{i}G_{j}(n)

其中G_{0}(n)=G_{0}(n-1)+Fib(n-1)+Fib(n-2)

所以我們維護G_{i}(n){\color{Red} },Fib(n-1),Fib(n-2)(0<=i<=k)共k+3個變量

 

知道了所有的G_{i}(n)後,知道F_{0}(n)=G_{0}(n) ,然後通過(-1)^{k}*F_{k}(n)=G_{k}(n)-\sum_{j=0}^{k-1}\binom{k}{j}n^{k-j}(-1)^{j}F_{j}(n)循環依次求得F_{i}(n),最終得出F_{k}(n)

代碼:

/**/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <stack>
#include <queue>

typedef long long LL;
using namespace std;

const long long mod = 998244353;

int k, M;
LL n, c[105][105], a[105][105], p[105], fj[105], F[105];

void init(){
	c[0][0] = 1;
	for (int i = 0; i <= k; i++){
		c[i][0] = 1;
		for (int j = 1; j <= i; j++){
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
		}
	}
	p[0] = 1;
	for (int i = 1; i <= k; i++) p[i] = n % mod * p[i - 1] % mod;
}

void mul(LL a[105][105], LL b[105][105]){
	LL c[M + 1][M + 1];
	memset(c, 0, sizeof(c));
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			for (int k = 0; k <= M; k++){
				c[i][j] = (c[i][j] + a[i][k] * b[k][j] % mod) % mod;
			}
		}
	}
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			a[i][j] = c[i][j];
		}
	}
}

LL solve(LL num){
	if(num == 1) return 1;
	for (int i = 0; i < k; i++){
		for (int j = i; j <= k; j++){
			a[i][j] = c[k - i][k - j];
		}
	}
	a[k][k] = a[k][k + 1] = a[k][k + 2] = 1;
	a[k + 1][k + 1] = a[k + 1][k + 2] = a[k + 2][k + 1] = 1;
	LL res[105][105];
	for (int i = 0; i <= M; i++){
		for (int j = 0; j <= M; j++){
			if(i == j) res[i][j] = 1;
			else res[i][j] = 0;
		}
	}
	
	while(num){
		if(num & 1) mul(res, a);
		mul(a, a);
		num >>= 1;
	}
	for (int i = 0; i <= k; i++){
		fj[k - i] = res[i][k + 2];
		// printf("fj = %d %lld\n", i, fj[k - i]);
	}
	for (int i = 0; i <= k; i++){
		LL sum = fj[i];
		for (int j = 0; j < i; j++){
			if(j & 1) sum = (sum + p[i - j] * F[j] % mod * c[i][j] % mod);
			else sum = (sum - p[i - j] * F[j] % mod * c[i][j] % mod);
		}

		if(i & 1) F[i] = -sum;
		else F[i] = sum;
	}
	return (F[k] % mod + mod) % mod;
}

int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);

	scanf("%lld %d", &n, &k);
	M = k + 2;
	init();
	printf("%lld\n", solve(n));

	return 0;
}
/**/

方法三:BM板子,首先列出前1000項放入BM板子,然後直接跑就行了,這裏代碼也不放了。滿足線性關係(G函數線性的,F函數即也是線性的)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章