【培训题】递推的矩阵优化 contest 矩阵应用 T4 5

Description

递推是动态规划实现方法之一,因此递推在OI中十分重要。在某信息学的分支学科中,LC学会的如何求一阶线性递推数列。他想深入学习此学科,希望知道求出N阶线性递推数列。为此,他理解到以下内容:

一个k阶线性递推式是这样的式子:(见图)<br>
其中f(1),f(2)...f(k)都是已知的。<br>
也就是说,这个数列的每一项都是由他之前连续k项相加所得。其中还包括一个常数C。例如,当k=2,a1=1,a2=1,c=0时,线性递推式为:f(n)=f(n-1)+f(n-2),这个式子就是我们熟悉的斐波那契数列。<br><br>

LC对如何去求这个式子一筹莫展,因此请你来帮助他。你的任务就是对于一个给定的k阶线性递推式,求出它的第N项f(n)mod m。


Input

输入包含多组数据。每组数据第一行为3个整数k,n,m。第二行为k个非负整数a1,a2,…,an。第三行为k个非负整数f(1),f(2),…,f(k)。输入结束标志为k=m=n=0。


Output

对于每组数据,输出f(n)mod m的值。


Hint

1<=k<=15 1<=n<=2^31-1 1<=m<=46340。


Solution

感觉很神奇查错查半天结果最后是错在初始化要反着初始化(因为他的系数是从大到小给的),最后mull()那个函数里面的val在赋值给anss[i]的时候忘记%了,加上去就A了,tkpl。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 20
using namespace std;
int k,m;
long long n;
long long aa[maxn],res[maxn],a[maxn][maxn],ans[maxn][maxn],ret[maxn][maxn],anss[maxn];
inline void Mull(){
	memset(ans,0,sizeof(ans));
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			long long val=0;
			for(int q=1;q<=k;q++){
				val+=(ret[i][q]%m*a[q][j]%m)%m;
			}
			ans[i][j]=val%m;
		}
	}
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			ret[i][j]=ans[i][j];
		}
	}
}
inline void Mull_Self(){
	memset(ans,0,sizeof(ans));
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			long long val=0;
			for(int q=1;q<=k;q++){
				val+=(a[i][q]%m*a[q][j]%m)%m;
			}
			ans[i][j]=val%m;
		}
	}
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			a[i][j]=ans[i][j];
		}
	}
}
inline void Quick_Pow(long long x){
	memset(ret,0,sizeof(ret));
	for(int i=1;i<=k;i++){
		ret[i][i]=1;
	}
	while(x){
		if(x&1)Mull();
		Mull_Self();
		x>>=1;
	}
}
inline void mull(){
	for(int i=1;i<=k;i++){
		long long val=0;
		for(int j=1;j<=k;j++){
			val+=(res[k-j+1]*ret[j][i])%m;
		}
		anss[i]=val%m;
	}
}
int main(){
	while(true){
		int flag=false;
		scanf("%d%lld%d",&k,&n,&m);
		if(k==0&&n==0&&m==0)break;
		for(int i=1;i<=k;i++){
			scanf("%lld",&aa[i]);
		}
		for(int i=1;i<=k;i++){
			scanf("%lld",&res[i]);
		}
		for(int i=1;i<=k;i++){
			if(n==i){
				printf("%lld\n",res[n]);
				flag=true;
				break;
			}
		}
		if(!flag){
			memset(a,0,sizeof(a));
			for(int i=1;i<=k;i++){
				a[i][1]=aa[i];
				if(i!=k)a[i][i+1]=1;
			}
			Quick_Pow(n-k);
			mull();
			printf("%lld\n",anss[1]);
		}
	}
	return 0;
}
posted @ 2019-01-07 17:47  虚拟北方virtual_north。  阅读(211)  评论(0编辑  收藏  举报