【山东省选】递归数列(洛谷P2461 [SDOI2008]递归数列)

文章目录


题目

描述
一个有自然数组成的数列按下式定义:
对于 i &lt; = k : A i = B i i&lt;=k:A_i=B_i i<=k:Ai=Bi
对于 i &gt; k : A i = C 1 A i − 1 + C 2 A i − 2 + … … C k A i − k i&gt;k:A_i=C_1A_{i-1}+C_2A_{i-2}+……C_kA_{i-k} i>k:Ai=C1Ai1+C2Ai2+CkAik
其中, B j 和 C j B_j和C_j BjCj ( 1 ≤ j ≤ k ) (1 \leq j \leq k) 1jk是给定的自然数。写一个程序,给定自然数 m &lt; = n m&lt;=n m<=n,计算
A m + A m + 1 + … … A n A_m+A_{m+1}+……A_n Am+Am+1+An,并输出它除以给定自然数 p p p的余数的值。
输入
第一行是自然数 k k k
第二行包括 k k k个自然数 b 1 , b 2 , … … , b k b_1,b_2,……,b_k b1b2bk
第三行包含 k k k个自然数 c 1 , c 2 , … … , c k c_1,c_2,……,c_k c1c2ck
第四行包含三个自然数 m , n , p m,n,p mnp
输出
一个正整数,表示 ( A m + A m + 1 + … … A n ) % p (A_m+A_{m+1}+……A_n)\%p Am+Am+1+An%p的值
样例输入
2
1 1
1 1
2 10 10003
样例输出
142

思路

我们设 S i S_i Si A i A_i Ai的前缀和,那么此题不就是求 S n − S m − 1 S_n-S_{m-1} SnSm1吗,
所以初始矩阵就为 [ A 1 , A 2 , … , A k , S k ] \begin{bmatrix} A_1,A_2,…,A_k,S_k\end{bmatrix} [A1A2AkSk]
A k + 1 = C 1 A k + C 2 A k − 1 + … … C k A 1 A_{k+1}=C_1A_k+C_2A_{k-1}+……C_kA_{1} Ak+1=C1Ak+C2Ak1+CkA1,而矩阵中有的如 A 1 A_1 A1要变成 A 2 A_2 A2,我们就把矩阵中的第一列的第二行记为一就行了,其他均如此,所以我们要乘的矩阵就为一个 k + 1 k+1 k+1的方阵
k + 1 个 [ 0 , 0 , … , 0 , C k , C k 1 , 0 , … , 0 , C k − 1 , C k − 1 0 , 1 , … , 0 , C k − 2 , C k − 2 ⋮ 0 , 0 , … , 1 , C 2 , C 2 0 , 0 , … , 0 , C 1 , C 1 ] ⏞ \begin{matrix} k+1个\\ \overbrace{ \begin{bmatrix} 0,0,…,0,C_k,C_k \\ 1,0 ,…,0,C_{k-1},C_{k-1}\\ 0,1 ,…,0,C_{k-2},C_{k-2}\\ \vdots \\ 0,0,…,1,C_2,C_2\\ 0,0,…,0,C_1,C_1 \end{bmatrix} }\end{matrix} k+10,0,,0,Ck,Ck1,0,,0,Ck1,Ck10,1,,0,Ck2,Ck20,0,,1,C2,C20,0,,0,C1,C1
读者可以发现,左下角是一个长宽为 k − 1 k-1 k1的单位矩阵,自己搞吧
但要注意,当 n , m ≤ k n,m\leq k n,mk时,你就只有自己搞个循环加了

代码

我是倒着存的
[ A k , … , A 2 , A 1 , S k ] \begin{bmatrix} A_k,…,A_2,A_1,S_k\end{bmatrix} [AkA2A1Sk]
因此我乘的矩阵就为
k + 1 个 [ C 1 , 1 , … 0 , C 1 C 2 , 0 , 1 … 0 , C 2 C 3 , 0 , 0 , 1 … 0 , C 3 ⋮ C k − 2 , 0 , 0 … , 1 , C k − 2 C k − 1 , 0 , 0 … , 0 , C k − 1 C k , 0 , 0 … , 0 , C k ] ⏞ \begin{matrix} k+1个\\ \overbrace{ \begin{bmatrix} C_1,1,…0,C_1 \\ C_2,0,1…0,C_2\\ C_3,0,0,1…0,C_3\\ \vdots \\ C_{k-2},0,0…,1,C_{k-2}\\ C_{k-1},0,0…,0,C_{k-1}\\ C_k,0,0…,0,C_k \end{bmatrix} }\end{matrix} k+1C1,1,0,C1C2,0,10,C2C3,0,0,10,C3Ck2,0,0,1,Ck2Ck1,0,0,0,Ck1Ck,0,0,0,Ck
中间是一个 k − 1 k-1 k1的单位矩阵

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long

LL n, m, k, ans, p;

struct Mx{
    int x, y;
    LL a[18][18];

    inline void cl(){
        memset(a, 0, sizeof(a));
    }

    Mx operator * (const Mx &b)const{
        Mx r;
        for(int i = 1; i <= x; i ++){
            for(int j = 1; j <= b.y; j ++){
                r.a[i][j] = 0;
                for(int k = 1; k <= b.x; k ++)
                    r.a[i][j] = (r.a[i][j]+b.a[k][j]*a[i][k]%p)%p;
            }
        }
        r.x = x;
        r.y = b.y;
        return r;
    }

    void print(){
        for(int i = 1; i <= x; i ++){
            for(int j = 1; j <= y; j ++)
                printf("%lld ", a[i][j]);
            printf("\n");
        }
    }

}A, one, F, M, N;

inline Mx qkp(Mx x, LL y){
    Mx sum = one;
    while( y ){
        if( y&1 )
            sum = x * sum;
        x = x * x;
        y >>= 1;
    }
    return sum;
}

int main(){
    scanf("%lld", &k);
    A.cl();
    one.cl();
    F.cl();
    LL sum = 0;
    one.x = one.y = k+1;
    A.x = 1, A.y = k+1;
    for(int i = 1; i <= k; i ++){
        scanf("%lld", &A.a[1][k-i+1]);
        one.a[i][i] = 1;
        sum += A.a[1][k-i+1];
    }
    one.a[k+1][k+1] = 1;
    A.a[1][k+1] = sum;
    F.x = F.y = k+1;
    for(int i = 1; i <= k; i ++){
        scanf("%lld", &F.a[i][1]);
        F.a[i][k+1] = F.a[i][1];
    }
    for(int i = 1; i <= k; i ++){
        if( !F.a[i][i+1] )
            F.a[i][i+1] = 1;
    }
    F.a[k+1][k+1] = 1;
    scanf("%lld%lld%lld", &m,&n,&p);
    if( n >= k ){
        N = A * qkp(F, 1ll*(n-k));
        ans = (ans + N.a[1][k+1])%p;
    }
    else{
        for(int i = 1; i <= n; i ++)
            ans = (ans+A.a[1][k-i+1])%p;
    }
    if( m > k+1 ){
        M = A * qkp(F, m-k-1);
        ans = (ans-M.a[1][k+1]+p)%p;
    }
    else{
        for(int i = 1; i < m; i ++)
            ans = ( ans - A.a[1][k-i+1] + p ) %p;
    }
    printf("%lld\n", (ans+p)%p);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值