1. 程式人生 > >Luogu-5004 專心OI-跳房子(矩陣快速冪)

Luogu-5004 專心OI-跳房子(矩陣快速冪)

cas ring isp clas pow 發現 pla 加速 line

Luogu-5004 專心OI-跳房子(矩陣快速冪)

題目鏈接

題解:

先考慮最樸素的dp
\(f[i][0/1]\)表示第\(i\)個位置跳/不跳的方案數,則:
\[ \begin{cases} f[i][0]=f[i-1][0]+f[i-1][1]\\f[i][1]=f[i-m-1][0]+f[i-m-1][1] \end{cases} \]
發現可以將\(f[i][0]+f[i][1]\)記為\(g[i]\),上式化為
\[ g[i]=g[i-1]+g[i-m-1] \]
很明顯可以用矩陣快速冪加速轉移:
\(g[i]\)可以轉移至下一次的\(g[i]\)
\(g[i-m]\)可以轉移至下一次的\(g[i]\)


\(g[i]\)可以轉移至下一次的\(g[i-1]\)
也就是說,構建一個這樣的矩陣:
\[ G= \left[ \begin{matrix} 0&0&0&\cdots&1\1&0&0&\cdots&0\0&1&0&\cdots&0\0&0&1&\cdots&0\&&\vdots\0&0&0&\cdots&1\\end{matrix} \right] \]
初始矩陣\(S[0][0]=1\)

代碼:

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline ll read(){
    ll ans=0,fh=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') fh=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        ans=ans*10+ch-'0',ch=getchar();
    return ans*fh;
}
const int P=1e9+7;
struct matrix{
    int a[20][20];
}S,G,base,Tmp;
int m;
ll n;
inline void build(){
    G.a[m][m]++,G.a[0][m]++;
    for(int i=1;i<=m;i++)
        G.a[i][i-1]++;
}
matrix operator * (matrix x,matrix y){
    for(int i=0;i<=m;i++)
        for(int j=0;j<=m;j++){
            Tmp.a[i][j]=0;
            for(int k=0;k<=m;k++)
                (Tmp.a[i][j]+=1ll*x.a[i][k]*y.a[k][j]%P)%=P;
        }
    return Tmp;
}
matrix poww(matrix x,ll y){
    for(int i=0;i<=m;i++)
        base.a[i][i]=1;
    while(y){
        if(y&1) base=base*x;
        x=x*x,y>>=1;
    }
    return base;
}
int main(){
//  freopen("nh.in","r",stdin);
//  freopen("zhy.out","w",stdout);
    n=read(),m=read();
    build(),G=poww(G,n+1);
    int Ans=0;
    for(int i=0;i<=m;i++)
        (Ans+=G.a[0][i])%=P;
    printf("%d\n",Ans);
    return 0;
}

Luogu-5004 專心OI-跳房子(矩陣快速冪)