【山东省选】递归数列(版本2)——矩阵加速

【山东省选】递归数列(版本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_{n}=\sum_ {i=1}^{k} C_{i}*A_{n-i}(n>k),而要求A(m~n)的和,可以求s(n)-s(m-1)(前缀和),所以得出关键递推式:

S_{n}=S_{n-1}+\sum_ {i=1}^{k} C_{i}*A_{n-i}(n>k)

所以我们不妨设初始矩阵为【A1  A2  ……  Ak  Sk】,

A_{2}=0*A_{1}+1*A_{2}

A_{3}=0*A_{1}+0*A_{2}+1*A_{3}

……

A_{k}=0+1*A_{k}

A_{k+1}=C_{k}*A_{1}+C_{k-1}*A_{2}+...+C_{1}*A_{k}

S_{k+1}=S_{k}+C_{k}*A_{1}+C_{k-1}*A_{2}+...+C_{1}*A_{k}

所以初始矩阵乘以(k+1)*(k+1)的加速矩阵:

\begin{bmatrix} A_{1} &A_{2} &... &A_{k} &S_{k} \end{bmatrix} * \begin{bmatrix} 0 &0 &... &C_{k} &C_{k} \\ 1 &0 &... &C_{k-1} &C_{k-1} \\ ... &... &... &...&... \\ 0 &0 &... &C_{1} &C_{1} \\ 0 &0 &... &0 & 1 \end{bmatrix} = \begin{bmatrix} A_{2} &A_{3} &... &A_{k+1} &S_{k+1} \end{bmatrix}

因为Ai=Bi(i≤k),所以可以变为

\begin{bmatrix} B_{1} &B_{2} &... &B_{k} &S_{k} \end{bmatrix} * \begin{bmatrix} 0 &0 &... &C_{k} &C_{k} \\ 1 &0 &... &C_{k-1} &C_{k-1} \\ ... &... &... &...&... \\ 0 &0 &... &C_{1} &C_{1} \\ 0 &0 &... &0 & 1 \end{bmatrix} = \begin{bmatrix} A_{2} &A_{3} &... &A_{k+1} &S_{k+1} \end{bmatrix}

所以\begin{bmatrix} B_{1} &B_{2} &... &B_{k} &S_{k} \end{bmatrix} * \begin{bmatrix} 0 &0 &... &C_{k} &C_{k} \\ 1 &0 &... &C_{k-1} &C_{k-1} \\ ... &... &... &...&... \\ 0 &0 &... &C_{1} &C_{1} \\ 0 &0 &... &0 & 1 \end{bmatrix}^{n-k} = \begin{bmatrix} A_{n-k+1} &A_{n-k+2} &... &A_{n} &S_{n} \end{bmatrix}

用两个矩阵快速幂分别求出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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/89310109