1. 程式人生 > >「P5004」專心OI - 跳房子 解題報告

「P5004」專心OI - 跳房子 解題報告

使用 block 開始 main struct a* init urn code

題面

\(N\)個無色格子排成一行,選若幹個格子染成黑色,要求每個黑色格子之間至少間隔\(M\)個格子,求方案數

思路:

矩陣加速

根據題面,這一題似乎可以用遞推

設第\(i\)個格子的編號為\(i\),有\(i\)個格子時的方案數為\(f(i)\)

顯然,當 \(i \le M+1\) 時,

可以所有格子不染色(方案數為\(1\)種,或者最多一個格子染色(方案數為\(i\)種)

所以有\(f(i)=i+1\)

\(i>M+1\)時,

對於第\(i\)個格子可以由第\(i-1\)個格子轉移過來,

而第\(i\)個格子有兩種情況

1、不染色,顯然可以這種情況下方案數為\(f(i-1)\)

2、染色,可以看出第\([i-m,i-1]\)個格子必定不染色,也就是沒有貢獻的,方案數為\(f(i-m-1)\)

但是!

\(N \le 10^{18}\)\(M \le 15\)

可以使用矩陣加速遞推

求解

我們要記錄的是應該是\(f(i) \to f(i+m)\)一共\(m+1\)個元素,於是就用一個\((M+1)^2\)的矩陣進行加速,配合快速冪求解

Code:

#include<bits/stdc++.h>
#define ll long long
#define Mod 1000000007
#define N 20
using namespace std;
int n;
ll b;
struct node{//矩陣放結構體裏
    ll f[N][N];
}res,a;
node operator* (const node a,const node b)//重載*運算
{
    int i,j,k;
    node c;ll res;
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            res=0;
            for(k=0;k<n;k++)
                res=(res+a.f[i][k]*b.f[k][j])%Mod;
            c.f[i][j]=res;
        }
    return c;
}
void init(){//初始化
    int i;
    for(i=0;i<n;i++) res.f[0][i]=i+2;//矩陣下標從0開始,所以+2
    for(i=0;i<n-1;i++) a.f[i+1][i]=1;
    a.f[n-1][n-1]=a.f[0][n-1]=1;
}
void quickPow(ll b)
{
    while(b)
    {
        if(b&1) res=res*a;
        b>>=1;a=a*a;
    }
}
int main()
{
    scanf("%lld%d",&b,&n);--b,++n;//res初始為b=1的情況,所以實際的b要-1
    init();quickPow(b);//n++是方便計算間隔
    printf("%lld",res.f[0][0]);
    return 0;
}

「P5004」專心OI - 跳房子 解題報告