「P5004」專心OI - 跳房子 解題報告
阿新 • • 發佈:2019-02-21
使用 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 - 跳房子 解題報告