[题解]luogu_P5004跳房子2(矩乘递推

一开始算出来了每个点的贡献,最后还要O(n)求和,而且递推式还带求和号(而且竟然还是倒着推的,不可矩乘优化

如果正着写:$g[i]= \sum_{j=1}^{i-m-1}g[j]$,i每后移一个只增加一个数,去掉求和号:$g[i]=g[i-1]+g[i-m-1]$

答案为g[i]的和+1(全空的情况

然而答案的递推式竟然是:$f[i]=f[i-1]+f[max(i-m-1,0)](f[0]=1)$,(特判$f[1]=2$,

几乎一样啊,只有它会从0转移不同....其实也好理解,如果这个点不染色那么和前面一样$f[i-1]$,如果染色那么前面m个都不会被染色,答案加上一个$f[i-m-1]$,如果$i<=m+1$那么染色方案只会加1,因为只能选一个点染(也就是这段$f[i]=i+1$

这样矩乘转移矩阵就显然了

不开longlong见祖宗

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,m;
struct mat{
    ll a[20][20];
    void clear(){
        memset(a,0,sizeof(a));
    }
    mat operator *(const mat&t)const{
        mat ans;ans.clear();
        for(int i=1;i<=m+1;i++)
        for(int j=1;j<=m+1;j++)
        for(int k=1;k<=m+1;k++)
        (ans.a[i][j]+=a[i][k]*t.a[k][j])%=mod;
        return ans;
    }
}ans,p;
mat operator ^(mat x,ll b){
    mat ans;ans.clear();for(int i=1;i<=m+1;i++)ans.a[i][i]=1;
    while(b){
        if(b&1)ans=ans*x;
        x=x*x;
        b>>=1;
    }
    return ans;
}
int main(){
    scanf("%lld%d",&n,&m);
    for(int i=1;i<=m+1;i++)ans.a[1][m+2-i]=i+1;
    p.a[1][1]=1;
    p.a[m+1][1]=1;
    for(int i=1;i<=m;i++)p.a[i][i+1]=1;
    if(n<=m+1){
        printf("%d",ans.a[1][m+2-n]);return 0;
    }
    p=p^(n-m-1);ans=ans*p;
    printf("%lld",ans.a[1][1]);
}

 

posted @ 2019-09-27 15:41  羊肉汤泡煎饼  阅读(157)  评论(0编辑  收藏  举报