公式来自:
http://blog.csdn.net/acdreamers/article/details/9409787
有些小错误改正了
矩阵乘法。任何线性k阶递推都能写成k阶矩阵形式。
⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢Siaiai−1ai−2...ai−k+1⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥
=
⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1000..0c1c110..0c2c201..0c3c300..0............ck−1ck−100..1ckck00..0⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥
⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢Si−1ai−1ai−2ai−3...ai−k⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥
=
⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1000..0c1c110..0c2c201..0c3c300..0............ck−1ck−100..1ckck00..0⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥i−k
⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢Skakak−1ak−2...a1⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥
直接对那个k+1阶矩阵快速幂即可,注意初始化矩阵为单位矩阵,即主对角线(左上到右下)都为1其他都为0。
#include<cmath>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define N 20
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int k,p,i,j;
ll s1[N][N],s2[N][N],q[N][N],f[N][N],g[N],res[N][N];
int b[N],c[N];
ll idx,l,r,r1,r2;
void multi(ll (*a)[N],ll (*b)[N])
{
int i,j,t;
fo(i,1,k+1) fo(j,1,k+1) s1[i][j] = a[i][j],s2[i][j] = b[i][j];
fo(i,1,k+1)
fo(j,1,k+1)
{
a[i][j] = 0;
fo(t,1,k+1) a[i][j] = (a[i][j] + s1[i][t] * s2[t][j]) % p;
}
}
int get_res(ll x)
{
int i,j,sum;
if (x <= k) {sum=0;fo(i,1,x)sum=(sum+b[i])%p;return sum;}
idx = x - k;
fo(i,1,k+1) fo(j,1,k+1) f[i][j] = q[i][j],res[i][j] = 0;
fo(i,1,k+1) res[i][i] = 1;
while (idx > 0)
{
if (idx&1) multi(res,f);
multi(f,f); idx = idx >> 1;
}
ll ans = 0;
fo(i,1,k+1) ans = (ans + res[1][i] * g[i]) % p;
return ans;
}
int main()
{
scanf("%d",&k);
fo(i,1,k) scanf("%d",&b[i]);
fo(i,1,k) scanf("%d",&c[i]);
fo(i,0,k-1) g[k-i+1] = b[i+1],g[1] += b[i+1];
q[1][1] = 1; fo(i,3,k+1) q[i][i-1] = 1;
fo(i,2,k+1) q[1][i] = q[2][i] = c[i-1];
scanf("%lld%lld%d",&l,&r,&p);
r1 = get_res(r);
if (l-1) r2 = get_res(l-1);
printf("%lld\n",(r1-r2+2*p)%p);
}