斐波那契求和(矩阵快速幂+多项式拆分)

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取余

介绍两种矩阵构造方法:

方法一:

,那么

构造一个一个矩阵维护 一共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;
}
/**/



方法二:

则有




其中

所以我们维护共k+3个变量


知道了所有的后,知道 ,然后通过循环依次求得,最终得出

代码:

/**/
#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函数即也是线性的)

全部评论
pjc大佬牛逼
点赞 回复 分享
发布于 2020-05-11 19:44

相关推荐

评论
点赞
收藏
分享
牛客网
牛客企业服务