bzoj 3231: [Sdoi2008]递归数列【矩阵乘法】

今天真是莫名石乐志
一眼矩阵乘法,但是这个矩阵的建立还是挺有意思的,就是把sum再开一列,建成大概这样

然后记!得!开!long!long!!

#include<iostream>
#include<cstdio>
using namespace std;
const int N=20;
long long n,b[N],c[N],sum,l,r,mod;
struct jz
{
    long long a[N][N];
    jz operator * (const jz &b) const
    {
        jz c;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                c.a[i][j]=0;
                for(int k=1;k<=n;k++)
                    c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
            }
        return c;
    }
}y;
long long wk(long long x)
{
    if(x<n)
    {
        long long r=0;
        for(int i=1;i<=x;i++)
            r=(r+b[i])%mod;
        return r;
    }
    x-=n-1;
    // cerr<<endl<<x<<endl;
    jz a=y,r;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            r.a[i][j]=i==j;
    while(x)
    {
        if(x&1)
            r=r*a;
        a=a*a;
        x>>=1;
    }
    // for(int i=1;i<=n;i++)
    // {
        // for(int j=1;j<=n;j++)
            // cerr<<r.a[i][j]<<" ";
        // cerr<<endl;
    // }
    long long ans=r.a[1][1]*sum%mod;
    for(int i=2;i<=n;i++)
        ans=(ans+r.a[1][i]*b[n-i+1]%mod)%mod;
    return ans;
}
int main()
{
    scanf("%lld",&n);
    n++;
    for(int i=1;i<n;i++)
        scanf("%lld",&b[i]);
    for(int i=1;i<n;i++)
        scanf("%lld",&c[i]);
    for(int i=2;i<=n;i++)
        y.a[1][i]=y.a[2][i]=c[i-1];
    y.a[1][1]=1;
    for(int i=3;i<=n;i++)
        y.a[i][i-1]=1;
    scanf("%lld%lld%lld",&l,&r,&mod);
    for(int i=1;i<n;i++)
        sum=(sum+b[i])%mod;
    // for(int i=1;i<=10;i++)
        // cerr<<wk(i)<<endl;
    printf("%lld\n",(wk(r)-wk(l-1)+mod)%mod);
    return 0;
}
posted @ 2018-08-01 22:17  lokiii  阅读(127)  评论(0编辑  收藏  举报