jzoj5836 Sequence 矩阵乘法+快速幂

Description


这里写图片描述
这里写图片描述

Solution


最后10min写出的24‘暴力还是很稳的,这个杯具提醒我们以后要提前看比赛结束时间和提前打暴力

考虑计数,如果不算那m个的话可以设f[i]表示以第i位为结尾的序列方案数,设last[i]为上一个a[i]的位置,那么有 fi=last[i]ji1fj f i = ∑ l a s t [ i ] ≤ j ≤ i − 1 f j ,这个可以前缀和一下做

现在我们需要加上m个数字。考虑要方案数最大,因此我们每次加入last最小的数字,使得统计到f中的这一项尽可能大

然后就非常套路了。m很大k很小,我们设一个k+1维向量保存前缀和来做矩阵乘法,搞一搞转移矩阵快速幂就可以了

改完题回头看会发现这么多的部分分并没有被我拿满。感觉这几天做的比赛都比较ok,但是由于各种原因不能拿到比较好看的分数,我果然还是太鶸了

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st,_=ed;i<=_;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int INF=0x3f3f3f3f;
const int MOD=1000000007;
const int N=1000005;

struct Matrix {
    LL arr[102][102];
} ;

LL f[N],sum[N],m;
int last[N],a[N];
int n,k;

LL read() {
    LL x=0,v=1; char ch=getchar();
    for (;ch<48||ch>57;v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<=57&&ch>=48;x=(x<<1)+(x<<3)+ch-48,ch=getchar());
    return x*v;
}

Matrix mul(Matrix a,Matrix b) {
    Matrix c;
    rep(i,1,k+1) rep(j,1,k+1) {
        c.arr[i][j]=0;
        rep(l,1,k+1) {
            c.arr[i][j]+=a.arr[i][l]*b.arr[l][j]%MOD;
        }
        c.arr[i][j]%=MOD;
    }
    return c;
}

Matrix ksm(Matrix x,LL dep) {
    Matrix ret=x; dep--;
    for (;dep;) {
        if (dep&1) ret=mul(ret,x);
        dep>>=1; x=mul(x,x);
    }
    return ret;
}

int main(void) {
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    n=read(),m=read(),k=read(); sum[0]=1;
    rep(i,1,n) a[i]=read();
    rep(i,1,n) {
        if (!last[a[i]]) f[i]=sum[i-1];
        else f[i]=sum[i-1]-sum[last[a[i]]-1];
        f[i]+=(f[i]<0)?MOD:0; f[i]-=(f[i]>=MOD)?MOD:0;
        sum[i]=(sum[i-1]+f[i]); sum[i]-=(sum[i]>=MOD)?MOD:0;
        last[a[i]]=i;
    }
    last[0]=INF;
    rep(i,n+1,n+k) {
        int pos=0;
        rep(j,1,k) if (last[j]<last[pos]) {
            pos=j;
        }
        if (!last[pos]) f[i]=sum[i-1];
        else f[i]=(sum[i-1]-sum[last[pos]-1]);
        f[i]+=(f[i]<0)?MOD:0; f[i]-=(f[i]>=MOD)?MOD:0;
        sum[i]=sum[i-1]+f[i]; sum[i]-=(sum[i]>=MOD)?MOD:0;
        last[pos]=i;
    }
    if (k>=m) {
        printf("%lld\n", (sum[n+m]+MOD-1)%MOD);
        return 0;
    }
    Matrix wjp; fill(wjp.arr,0);
    rep(i,1,k) wjp.arr[i+1][i]=1;
    wjp.arr[k+1][k+1]=2; wjp.arr[1][k+1]=MOD-1;
    Matrix ret=ksm(wjp,m-k); LL ans=0;
    rep(i,1,k+1) ans=ans+sum[n+i-1]*ret.arr[i][k+1]%MOD;
    printf("%lld\n", (ans+MOD-1)%MOD);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值