CF392C & Yet Another Number Sequence 题解

纪念一下完全凭借自己想出来的的第一道黑题。

思路

首先看到数据范围 n ≤ 1 0 17 n\leq 10^{17} n1017 就知道本题是矩阵快速幂或者推式子。

首先发现对于不同的 i , j i,j i,j f i × i k , f j × j k f_i\times i^{k},f_j\times j^{k} fi×ik,fj×jk 非常难合并,除非用出斐波那契通项公式,所以放弃这条道路,走矩阵快速幂道路。

考虑一个最简单的初始矩阵:

[ f i × i k f i + 1 × ( i + 1 ) k ] \begin{bmatrix}{f_i\times i^k} &{f_{i+1}\times (i+1)^k}\end{bmatrix} [fi×ikfi+1×(i+1)k]

我们要转移到的目标矩阵:

[ f i + 1 × ( i + 1 ) k f i + 2 × ( i + 2 ) k ] \begin{bmatrix}{f_{i+1}\times {(i+1)}^k} &{f_{i+2}\times (i+2)^k}\end{bmatrix} [fi+1×(i+1)kfi+2×(i+2)k]

我们发现这是不可行的。因为 ( i + 2 ) k (i+2)^k (i+2)k 不是 ( i + 1 ) k (i+1)^k (i+1)k 且增广矩阵中的量必须为常数的缘故,转移到第一个数十分简单,但是第二个数就很困难了。

于是我们尝试着倒退,将 f i + 2 × ( i + 2 ) k f_{i+2}\times (i+2)^k fi+2×(i+2)k 拆成与 f i × i k   f i + 1 × ( i + 1 ) k f_{i}\times i^k \ f_{i+1} \times (i+1)^k fi×ik fi+1×(i+1)k 有关的数,并尝试将其加入初始矩阵与增广矩阵中。

f i + 2 × ( i + 2 ) k f_{i+2}\times(i+2)^k fi+2×(i+2)k

= f i × ( i + 2 ) k + f i + 1 × ( i + 2 ) k =f_{i}\times(i+2)^k+f_{i+1}\times (i+2)^k =fi×(i+2)k+fi+1×(i+2)k

由二项式定理:

( i + 2 ) k = ( ( i + 1 ) + 1 ) k = ∑ j = 0 k ( k j ) ( i + 1 ) k (i+2)^k=((i+1)+1)^k=\sum\limits_{j=0}^k{\dbinom{k}{j}(i+1)^k} (i+2)k=((i+1)+1)k=j=0k(jk)(i+1)k

( i + 2 ) k = ∑ j = 0 k ( k j ) i j × 2 j − k (i+2)^k=\sum\limits_{j=0}^k{\dbinom{k}{j}i^j\times2^{j-k}} (i+2)k=j=0k(jk)ij×2jk

将上式子带回原式:

= f i × ∑ j = 0 k ( k j ) i j × 2 j − k + f i + 1 × ∑ j = 0 k ( k j ) ( i + 1 ) k =f_i\times\sum\limits_{j=0}^k{\dbinom{k}{j}i^j\times2^{j-k}}+f_{i+1}\times \sum\limits_{j=0}^k{\dbinom{k}{j}(i+1)^k} =fi×j=0k(jk)ij×2jk+fi+1×j=0k(jk)(i+1)k

将外面的乘进去:

= ∑ j = 0 k f i × i j ( k j ) 2 j − k + ∑ j = 0 k f i + 1 × ( i + 1 ) j ( k j ) =\sum\limits_{j=0}^k{f_i\times i^j\dbinom{k}{j}2^{j-k}}+\sum\limits_{j=0}^k{f_{i+1}\times (i+1)^j\dbinom{k}{j}} =j=0kfi×ij(jk)2jk+j=0kfi+1×(i+1)j(jk)

你会惊奇的发现:加和中的 ( k j ) \dbinom{k}{j} (jk) 以及 ( k j ) 2 j − k \dbinom{k}{j}2^{j-k} (jk)2jk 均为可以提前预处理的常数。结合 k k k 又非常小,可以将 $p\in [0,k] $ f i × i p f_i\times i^{p} fi×ip 全部预处理出来,塞进初始矩阵里就可以了。

矩阵大小为 40 × 40 40\times 40 40×40,一次矩阵乘法复杂度为 4 0 3 40^3 403,所以总时间为 6400 × log ⁡ 2 ( 1 0 17 ) < 57 ∗ 6400 6400\times\log_2(10^{17})<57*6400 6400×log2(1017)<576400 可过

还有,题目中要求求的是和,你还得在初始矩阵中额外添加一个和的数据,

代码

#include <bits/stdc++.h>
using namespace std;

#define MAXN 40
#define int long long 

const int mod = 1e9 + 7;

struct matrix {
	int n, m;
	int a[(MAXN<<1)+5][(MAXN<<1)+5];
	matrix() { memset(a, 0, sizeof a); }
} ans, p;

void debug(matrix x) {
	cout << x.n << ' ' << x.m << ":\n";
	for (int i = 1; i <= x.n; ++i) {
		for (int j = 1; j <= x.m; ++j) cout << x.a[i][j] <<' ';
		cout << endl;
	}
}

matrix operator * (matrix x, matrix y) {
	matrix z;
	z.n = x.n, z.m = x.m;
	for (int i = 1; i <= x.n; ++i) 
		for (int j = 1; j <= y.m; ++j)
			for (int k = 1; k <= x.m; ++k)
				(z.a[i][j] += x.a[i][k] * y.a[k][j] % mod) %= mod;
	return z;
}

matrix mpow(matrix x, int y) {
	matrix ans;
	ans.n = x.n, ans.m = x.m;
	for (int i = 1; i <= min(ans.n, ans.m); ++i) ans.a[i][i] = 1; 
	while (y) {
		if (y & 1) ans = ans * x;
		x = x * x;
		y >>= 1;
	}
	return ans;
}

int mpow(int x, int y) {
	if (y < 0) return 0;
	int s = 1;
	while (y) {
		if (y & 1) s = s * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return s;
}

int C(int a, int b) {
	if (a < b) return 0;
	if (b > a / 2) b = a - b;
	int s = 1;
	for (int i = a - b + 1; i <= a; ++i) s = s * i % mod;
	for (int i = 1; i <= b; ++i) s = s * mpow(i, mod - 2) % mod;
	return s;
}

int n, k;

signed main() {
	cin >> n >> k;
	if (n == 1) {
		puts("1");
		return 0;
	} 
	ans.n = 1, ans.m = 2 * k + 3;
	for (int i = 0; i <= k; ++i) 
		ans.a[1][i + 1] = 1, ans.a[1][i + k + 2] = 2 * mpow(2, i) % mod;
	ans.a[1][2 * k + 3] = (1 + mpow(2, k + 1)) % mod;
	p.n = p.m = 2 * k + 3;
	for (int j = 1; j <= 2 * k + 3; ++j) {
		if (j <= k + 1) 
			p.a[j + k + 1][j] = 1;
		else if (j <= 2 * k + 2) {
		 	// F[i] * i ^ p
		 	// F[i + 1] * (i + 1) ^ p
		 	// ↓
			// F[i + 1] * (i + 2) ^ p + F[i] * (i + 2) ^ p 
			for (int i = k + 2; i <= 2 * k + 2; ++i) // F[i + 1]
				p.a[i][j] = C(j - k - 2, i - k - 2);
			for (int i = 1; i <= k + 1; ++i) 
				p.a[i][j] = mpow(2, j - k - 1 - i) * C(j - k - 2, i - 1) % mod;
		}
		else {
			for (int i = k + 2; i <= 2 * k + 2; ++i) // F[i + 1]
				p.a[i][j] = C(k, i - k - 2);
			for (int i = 1; i <= k + 1; ++i) 
				p.a[i][j] = mpow(2, k + 1 - i) * C(k, i - 1) % mod;
			p.a[2 * k + 3][j] = 1;
		}
	}
//	debug(ans), debug(p);
	ans = ans * mpow(p, n - 2);
//	debug(ans);
	cout << ans.a[1][2 * k + 3] << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值