【BZOJ5505】[GXOI/GZOI2019]逼死強迫症(矩陣快速冪)

【BZOJ5505】[GXOI/GZOI2019]逼死強迫症(矩陣快速冪)

題面

BZOJ
洛谷php

題解

若是沒有那兩個\(1*1\)的東西,答案就是斐波那契數,能夠簡單的用\(dp\)獲得。
大概是設\(f[i]\)表示當前除了到第\(i\)列的方案數,轉移是考慮用\(2*1\)豎着覆蓋一列仍是\(2\)\(1*2\)橫着覆蓋兩列,獲得轉移\(f[i]=f[i-1]+f[i-2]\)
如今回假設要在這一行放上第二個\(1*1\),那麼直到前一個\(1*1\)所在列以前的全部方塊都被惟一肯定了,而左側就是隨便放的方案數,即斐波那契數列。
\(g[i]\)表示上面的\(f[i]\)\(f[i]\)爲答案,\(s[i]\)\(g[i]\)前綴和。
那麼\(f[i]=f[i-1]+f[i-2]+2h[i-3]\),即考慮當前在哪一行放上這個\(1*1\)的東西,有兩種方案數,接下來能夠在任何一個地方把這個東西補齊,而前面能夠任意擺放。
根據斐波那契數列的性質,\(h[i-3]=g[i-1]-1\),因此拿矩陣快速冪維護一下斐波那契數列和\(dp\)數列就好了。ios

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MOD 1000000007
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
const int N=5;
struct Matrix
{
    int s[N][N];
    void clear(){memset(s,0,sizeof(s));}
    void init(){clear();for(int i=0;i<N;++i)s[i][i]=1;}
    int*operator[](int x){return s[x];}
}A[32],B,P,Ans;
Matrix operator*(Matrix a,Matrix b)
{
    Matrix c;c.clear();
    for(int i=0;i<N;++i)
        for(int j=0;j<N;++j)
            for(int k=0;k<N;++k)
                c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j])%MOD;
    return c;
}
Matrix fpow(int b)
{
    Matrix s;s.init();
    for(int i=0;b;++i,b>>=1)
        if(b&1)s=s*A[i];
    return s;
}
int main()
{
    B[0][2]=B[0][3]=B[0][4]=1;
    P[0][1]=P[1][0]=P[1][1]=P[2][3]=P[3][2]=P[3][3]=P[4][4]=1;
    P[2][1]=P[3][1]=2;P[4][1]=MOD-2;
    A[0]=P;for(int i=1;i<32;++i)A[i]=A[i-1]*A[i-1];
    int T=read();
    while(T--)
    {
        int n=read();
        if(n==1){puts("0");continue;}
        if(n==2){puts("0");continue;}
        Ans=B*fpow(n-2);
        printf("%d\n",Ans[0][1]);
    }
    return 0;
}
相關文章
相關標籤/搜索