【Codeforces 575A】Fibonotci

线段树,矩阵快速幂。

题目大意:

给定一个无限长的数列 $s$,给出前 $n$ 项 $s_0,s_1,\ldots,s_{n-1}$。之后的项满足 $s_i=s_{i\bmod n}$。

再给出一个无限长的数列 $f$,$f_0=0,f_1=1$,对于 $i\geq 2$,$f_i=s_{i-1}f_{i-1}+s_{i-2}f_{i-2}$。

现在有 $m$ 个位置的 $s$ 不满足 $s_i=s_{i\bmod n}$ 而是被钦定为了一个值。

求 $f_k$ 模 $p$ 的结果。

题解:

考虑不存在不满足循环条件的数时的做法。

这个数列显然可以用矩阵乘法进行转移。我们发现每 $n$ 个转移矩阵都是一样的,所以可以用乘法结合律合并,然后对 $n$ 个的乘积一起进行矩阵快速幂。最后一段不完整的可以直接暴力乘起来。时间复杂度 $O(n+\log k)$。

如果存在不满足循环条件的数,那么原数列相当于被分成了 $O(m)$ 个段,每段里我们可以用矩阵快速幂和暴力进行求值。然后依次相乘即可。这样的时间复杂度为 $O(nm+m\log k)$。

由于现在有 $m$ 个段,所以不完整的部分我们不能再暴力了。我们使用线段树维护区间乘积,然后不完整的段的乘积就可以 $O(\log n)$ 求得。

这样时间复杂度就是 $O(n+m(\log n+\log k))$。

Code:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=114514;
LL k;int md,n,s[N],m;
struct qwq{
    LL x;int v;
    inline bool operator<(const qwq&rhs)const{return x<rhs.x;}
}B[N];
struct mat{
    int A[2][2];
    inline int*operator[](const int&x){return A[x];}
    inline mat(){A[0][0]=A[0][1]=A[1][0]=A[1][1]=0;}
    inline mat(const int&a,const int&b,const int&c,const int&d){
        A[0][0]=a,A[0][1]=b,A[1][0]=c,A[1][1]=d;
    }
    inline mat operator*(const mat&b)const{
        mat c;
        c[0][0]=((LL)A[0][0]*b.A[0][0]+(LL)A[0][1]*b.A[1][0])%md;
        c[0][1]=((LL)A[0][0]*b.A[0][1]+(LL)A[0][1]*b.A[1][1])%md;
        c[1][0]=((LL)A[1][0]*b.A[0][0]+(LL)A[1][1]*b.A[1][0])%md;
        c[1][1]=((LL)A[1][0]*b.A[0][1]+(LL)A[1][1]*b.A[1][1])%md;
        return c;
    }
}d[N<<2],f;
vector<pair<LL,mat> >vec;
void build(int l,int r,int o){
    if(l==r)d[o]=mat(0,s[l-1],1,s[l]);else{
        const int mid=l+r>>1;
        build(l,mid,o<<1),build(mid+1,r,o<<1|1);
        d[o]=d[o<<1]*d[o<<1|1];
    }
}
void query(int l,int r,int o,const int&L,const int&R,mat&f){
    if(L<=l&&r<=R)f=f*d[o];else{
        const int mid=l+r>>1;
        if(L<=mid)query(l,mid,o<<1,L,R,f);
        if(mid<R)query(mid+1,r,o<<1|1,L,R,f);
    }
}
void pow(mat a,LL b,mat&f){
    for(;b;b>>=1,a=a*a)
    if(b&1)f=f*a;
}
int main(){
    scanf("%lld%d%d",&k,&md,&n);
    for(int i=0;i<n;++i)scanf("%d",s+i),s[i]%=md;
    s[n]=*s;
    f=mat(0,1,0,0);
    build(1,n,1);
    scanf("%d",&m);
    for(int i=1;i<=m;++i)scanf("%lld%d",&B[i].x,&B[i].v),B[i].v%=md;
    sort(B+1,B+m+1);
    for(int i=1;i<=m;++i)if(B[i].x<=k){
        if(i==1||B[i].x!=B[i-1].x+1)
        vec.push_back(make_pair(B[i].x,mat(0,s[(B[i].x-1)%n],1,B[i].v)));else
        vec.back().second.A[1][1]=B[i].v;
        if(B[i].x+1<=k)
        vec.push_back(make_pair(B[i].x+1,mat(0,B[i].v,1,s[(B[i].x+1)%n])));
    }
    int it=0;
    for(LL nw=0;nw!=k;){
        LL nxt=(it==vec.size()?k:vec[it].first-1);
        if(nw/n!=nxt/n){
            int m=nw%n+1;
            query(1,n,1,m,n,f);
            nw=nw-(nw%n)+n;
            if(nw/n!=nxt/n)pow(d[1],nxt/n-nw/n,f),nw=nxt/n*n;
        }
        if(nxt%n)query(1,n,1,nw%n+1,nxt%n,f);
        nw=nxt;
        if(nw==k)break;
        f=f*vec[it++].second;
        ++nw;
    }
    printf("%d\n",f[0][0]);
    return 0;
}