【山东省选】递归数列(版本2)
时间限制: 1 Sec 内存限制: 128 MB
题目描述
一个有自然数组成的数列按下式定义:
对于i<=k:Ai=Bi
对于i>k:Ai=C1Ai-1+C2Ai-2+……CkAi-k
其中,Bj和Cj(1<=j<=k)是给定的自然数。写一个程序,给定自然数m<=n,计算Am+Am+1+……An,并输出它除以给定自然数p的余数的值。
输入
第一行是自然数k
第二行包括k个自然数b1,b2,……bk。
第三行包含k个自然数c1,c2,……ck。
第四行包含三个自然数m,n,p。
输出
一个正整数,表示(Am+Am+1+……An)%p的值
样例输入
2
1 1
1 1
2 10 10003
样例输出
142
提示
对于100%的测试数据:1<=k<=15;1<=m<=n<=10^18
对于20%的测试数据:1<=k<=15;1<=m<=n<=10^6
对于30%的测试数据:k=1;1<=m<=n<=10^18
对于所有测试数据:0<=b1,b2,……,bk,c1,c2,……,ck<=10^9;1<=p<=10^8.
题解
由题可知,而要求A(m~n)的和,可以求s(n)-s(m-1)(前缀和),所以得出关键递推式:
所以我们不妨设初始矩阵为【A1 A2 …… Ak Sk】,
……
所以初始矩阵乘以(k+1)*(k+1)的加速矩阵:
因为Ai=Bi(i≤k),所以可以变为
所以
用两个矩阵快速幂分别求出Sn和Sm-1,两数相减加上p再%p。
注意n≤k和m-1≤k时只能暴力算出来。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int k;
ll n,m,p,Sum[20];
struct matrix{
int n,m;
ll c[20][20];
matrix(){memset(c,0,sizeof(c));n=m=0;}
matrix operator*(const matrix&a){
matrix r;r.n=n,r.m=a.m;
for(int i=1;i<=n;i++)
for(int j=1;j<=r.m;j++)
for(int l=1;l<=m;l++)r.c[i][j]=(r.c[i][j]+c[i][l]*a.c[l][j])%p;
return r;
}
matrix mpow(matrix a,ll b){
matrix rs;rs.n=rs.m=a.n;
for(int i=1;i<=rs.n;i++)rs.c[i][i]=1;
for(;b;b>>=1){
if(b&1)rs=rs*a;
a=a*a;
}
return rs;
}
}a,b;
int main()
{
scanf("%d",&k);
a.n=1;a.m=k+1;
b.n=b.m=k+1;
for(int i=1;i<=k;i++)scanf("%lld",&a.c[1][i]),Sum[i]=Sum[i-1]+a.c[1][i];
a.c[1][k+1]=Sum[k];
for(int i=k;i>=1;i--)scanf("%lld",&b.c[i][k]),b.c[i][k+1]=b.c[i][k];
scanf("%lld%lld%lld",&n,&m,&p);
for(int i=1;i<k;i++)b.c[i+1][i]=1;
b.c[k+1][k+1]=1;
ll x,y;
if(m>k){
matrix c=a*b.mpow(b,m-k);
y=c.c[1][k+1]%p;
}
else y=Sum[m]%p;
if(n-1>k){
matrix c=a*b.mpow(b,n-1-k);
x=c.c[1][k+1]%p;
}
else x=Sum[n-1]%p;
printf("%lld",(y-x+p)%p);
putchar('\n');
return 0;
}