「SPOJ SEQ」 Recursive Sequence

Description

定义数列 (a) 的第 (i)(a_i) 的公式为:

(egin{cases} a_i = b_i & i le k \ a_i = sumlimits_{t=1}^k a_{i-t} imes c_{t} & i > k end{cases})

现给定 (k,b={b_1, b_2, cdots ,b_k},c={c_1, c_2, cdots ,c_k},n) ,求 (a_n mod 10^9) 的值。

Hint

(1le kle 10)

(1le nle 10^9)

(0le b_i, c_ile 10^9)

Solution

不错的矩阵优化递推 (k) 项递推式的板子。没有常数项更加简单。

首先,对于 (n le k) 的情况,直接输出 (b_n) 的值。

对于 (n > k) 的情况,需要使用矩阵加速。

[egin{bmatrix} a_i \ a_{i-1} \ vdots \ a_{i-k} end{bmatrix} = egin{bmatrix} c_1 & cdots & c_{k-1} & c_k \ 1 & cdots & 0 & 0 \ vdots & ddots & vdots & vdots \ 0 & cdots & 1 & 0 end{bmatrix} imes egin{bmatrix} a_{i-1} \ a_{i-2} \ vdots \ a_{i-k-1} end{bmatrix} ]

这种题的套路差不多就是:最上面一排系数,下面斜着放 1。

怎么理解这个式子呢?

新矩阵的 (a_i) 项是由旧矩阵中所有元素分别乘上系数的和而来,而新矩阵的其他元素可以直接由旧矩阵的元素那里拿过来,并且下移一位。

这样时间复杂度是 (O(k^3 log n)) 的。

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : SPOJ SEQ Recursive Sequence
 */
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int mod = 1e9;
typedef long long LL;
typedef vector<LL> vec;
typedef vector<vec> mat;

inline mat operator * (mat a,mat b) {
	mat x(a.size(), vec(b[0].size(), 0));
	for (register int i = 0; i < a.size(); i++)
		for (register int j = 0; j < b[0].size(); j++)
			for (register int k = 0; k < b.size(); k++)
				(x[i][j] += a[i][k] * b[k][j]) %= mod;
	return x;
}

inline mat matI(int n) {
	mat x(n, vec(n, 0));
	for (register int i = 0; i < n; i++)
		x[i][i] = 1;
	return x;
}

mat fastpow(mat base, int k) {
	if (!k) return matI(base.size());
	mat x = fastpow(base, k >> 1);
	if (k & 1) return (x * x) * base;
	else return x * x;
}

void solve() {
	int n, k;
	cin >> k;
	mat b(k, vec(1, 0)), c(k, vec(1, 0));
	for (register int i = 0; i < k; i++)
		cin >> b[i][0];
	for (register int i = 0; i < k; i++)
		cin >> c[i][0];
	cin >> n;
	
	if (n <= k) cout << b[n - 1][0] << endl;
	else {
		mat base(k, vec(k, 0));
		for (register int i = 0; i < k; i++)
			base[0][i] = c[i][0];
		for (register int i = 1; i < k; i++)
			base[i][i - 1] = 1;
		reverse(b.begin(), b.end());
		b = fastpow(base, n - k) * b;
		cout << b[0][0] << endl;
	}
}

signed main() {
	int T;
	cin >> T;
	while (T--)	solve();
	return 0;
}
原文地址:https://www.cnblogs.com/-Wallace-/p/12671912.html