[矩阵乘法]JZOJ 6275 小L的数列

Description

 

Input

一行两个整数n和k。

之后1行k个正整数b1...bk。

之后1行k个正整数f1...fk。
 

Output

输出一个整数表示fn
 

Sample Input

【样例输入1】

5 4

1 2 3 4

4 3 2 1

【样例输入2】

100000 4

1 2 3 4

12 23 34 45

 

Sample Output

【样例输出1】

27648

样例输出2】

33508797
 
 

Data Constraint

对于30%的数据,n≤10000.

对于另外20%的数据,bi=1,n≤1000000.

对于另外20%的数据,f[1]...f[k-1]=1.

对于另外20%的数据,k≤30.

对于100%的数据,1≤k≤200,1≤n≤40000000,1≤bi,fi≤998244352.
 
 

Hint

样例解释:1*2*2*3*3*3*4*4*4*4=27648

分析

N大K小比较显然是矩阵乘法

我们发现最终答案一定可以由原来的f1-fk的幂的乘积构成

那么考虑计算指数,设矩阵A为i-k~i-1的某个f在各个位置上的指数,那么转移矩阵C为

0 0 0 bk

1 0 0 bk-1

0 1 0 bk-2

0 0 1 bk-3

0 0 0 bk-4

看起来时间复杂度是k^4logn的?因为每个初始f都要处理一次,但是不难发现变化的是矩阵A,C是不变的,所以可以预处理出来C后统计答案

 

减小指数要用费马小定理

 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll P=998244353;
const int N=210;
int n,k;
struct Rect {
    ll a[N][N];
    void In() {
        for (int i=1;i<=k;i++) a[i][i]=1;
    }
    void Empty() {memset(a,0,sizeof a);}
}C,c;
Rect operator * (Rect a,Rect b) {
    c.Empty();
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++)
            for (int l=1;l<=k;l++)
                (c.a[i][j]+=a.a[i][l]*b.a[l][j]%(P-1ll))%=(P-1ll);
    return c;
}
ll f[N],ans;

void Pow(int y) {
    Rect x=C;C.Empty();C.In();
    for (;y;y>>=1) {
        if (y&1) C=C*x;
        x=x*x;
    }
}

ll Pow1(ll x,ll y) {ll ans=1;for (;y;y>>=1ll,x=x*x%P) if (y&1ll) ans=ans*x%P;return ans;}

int main() {
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%d%d",&n,&k);
    for (int i=k;i;i--) scanf("%lld",&C.a[i][k]);
    for (int i=1;i<=k;i++) scanf("%lld",&f[i]);
    for (int i=2;i<=k;i++) C.a[i][i-1]=1;
    if (n<=k) {
        printf("%lld",f[n]);
        return 0;
    }
    Pow(n-k);ans=1;
    for (int i=1;i<=k;i++) ans=(ans*Pow1(f[i],C.a[i][k]))%P;
    printf("%lld",ans);
}
View Code

 

posted @ 2019-08-09 07:23  Vagari  阅读(298)  评论(0编辑  收藏  举报