【SSL 1532】递推【矩阵乘法の变式】

递推

T i m e Time Time L i m i t : 10000 M S Limit:10000MS Limit:10000MS
M e m o r y Memory Memory L i m i t : 65536 K Limit:65536K Limit:65536K
C a s e Case Case T i m e Time Time L i m i t : 1000 M S Limit:1000MS Limit:1000MS

Description

动态规划的实现形式之一是递推,因此递推在oi中十分重要。在某信息学的分支学科中,LC学会了如何求一阶线性递推数列。由于他现在正在学习主干学科,因此希望知道求出N阶线性递推数列。为此,他了解到以下内容:
一个N阶线性递推式是这样的式子:
  F i = a 0 ∗ F i − n + a 1 ∗ F i − ( n − 1 ) + . . . + a n − 1 ∗ F i − 1 + a n F_i=a_0*F_{i-n}+a_1*F_{i-(n-1)}+...+a_{n-1}*F_{i-1}+a_n Fi=a0Fin+a1Fi(n1)+...+an1Fi1+an
  也就是说,这个数列的每一项都是由他之前的连续N项加权相加所得。其中还包括一个常数an。
  例如,当 N = 2 , a 0 = a 1 = 1 , a 2 = 0 N=2,a_0=a_1=1,a_2=0 N=2a0=a1=1a2=0时,这个式子就是我们熟悉的斐波那契数列。当然,作为边界条件。 f 0 , f 1 , . . . f n − 1 f_0,f_1,...f_{n-1} f0f1...fn1都是已知的。Lc对如何去求这个式子一筹莫展,因此他想请你帮忙。你的任务是对于一个给定的N阶线性递推式,求出他的第k项。

Input

第一行两个整数:n,k。其中n表示这是一个N阶线性递推式,k表示你需要球的那一项。
第二行有n+1个整数:a0,a1,…an,表示这个递推式的系数。
第三行有n个整数:f0,f1,…,fn-1表示数列的初始值。

Output

只有一行,其中只有一个整数,表示这个数列第k项的值。由于数据较大,你只需输出 m o d mod mod 9973 9973 9973的值。

Sample Input

2 10
1 1 0
0 1

Sample Output

55

分析:

需要设一个 1 × ( n + 1 ) 1×(n+1) 1×(n+1)初始矩阵
n n n项就是输入的 f i − 1 f_{i-1} fi1 n + 1 n+1 n+1项就是 f n f_n fn要加上的 a n a_n an
考虑构造 ( n + 1 ) × ( n + 1 ) (n+1)×(n+1) (n+1)×(n+1)变换矩阵
n n n项之前的所有 f i f_i fi就直接变到 f i + 1 f_{i+1} fi+1即可
到第 n n n项时 公式是前面的 f i f_i fi乘对应的系数 再加常数
所以把常数正常填进去 矩阵乘法法则就是乘上常数
n + 1 n+1 n+1位直接加上常数 做 k k k次快速幂因为从 f 0 f_0 f0开始

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define reg register
using namespace std;
typedef long long ll;
const int N=11;
const int mod=9973;
ll n,k;
ll num[N];
struct matrix{
    
    
	ll n,m;
	ll F[N][N];
}A,B,C;
matrix operator *(matrix a,matrix b)
{
    
    
	matrix ans;
	memset(ans.F,0,sizeof(ans.F));
	ans.m=a.m;ans.n=b.n;
	for(reg int k=1;k<=a.m;k++)
		for(reg int i=1;i<=ans.n;i++)
			for(reg int j=1;j<=ans.m;j++)
				ans.F[i][j]=(ans.F[i][j]+a.F[i][k]*b.F[k][j]%mod)%mod;  //矩阵乘
	return ans;
}
void ksm(ll x)
{
    
    
	if(x==1){
    
    
		B=A;
		return;
	}
	ksm(x>>1);
	B=B*B;
	if(x&1) B=B*A;
}
void Pre()
{
    
    
	scanf("%lld%lld",&n,&k);
	for(reg int i=1;i<=n+1;i++)
		scanf("%lld",&num[i]);
	C.n=1;C.m=n+1;
	for(reg int i=1;i<=n;i++)
		scanf("%lld",&C.F[1][i]);  //前n项就是
	C.F[1][n+1]=num[n+1];  //n+1项就是常数
	A.n=n+1;A.m=n+1;
	for(reg int i=1;i<n;i++)
		A.F[i+1][i]=1;  //前面的fi直接转移
	for(int i=1;i<=n;i++)
		A.F[i][n]=num[i];  //乘上常数
	A.F[n+1][n]=1;A.F[n+1][n+1]=1;  //加上系数
} 
int main(){
    
    
	Pre();ksm(k);C=C*B;
	printf("%lld",C.F[1][1]);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/111400702
SSL