方格填色
题面描述
给一个\(m\cdot n\)的方格,每个格子可以是黑色或者白色。要求左右相邻两格不能同为白色且相邻两列不能全为黑色。求满足条件的方案数.\(1\leq m \leq 5 , 1\leq n\leq 10^{18}\)
题解
因为\(m\)很小,所以可以每列每列的考虑,每列可以用状压来表示,黑色为1,白色为0,则两列\(i|j=2^m-1,且两列不为2^m-1\)
则可以写出dp方程转移
\(dp[i][st]=\sum_{j=0}^{2^m-1}[j|st==2^m-1]\cdot dp[i-1][j] (st!=2^m-1)\)
\(dp[i][st]=\sum_{j=0}^{2^m-2}dp[i-1][j] (st==2^m-1)\)
显然可以写成矩阵乘法形式
\[\begin{pmatrix}
dp[i][0]\\
dp[i][1]\\
···\\
dp[i][2^m-1]\\
\end{pmatrix}=\begin{pmatrix}\\ \\ \\ \\ \end{pmatrix}*\begin{pmatrix}dp[i-1][0]\\dp[i-1][1]\\···\\dp[i-1][2^m-1]\end{pmatrix}\]
所以只要预处理中间的矩阵就行
时间复杂度为\(O((2^m)^3\cdot logn)\)
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#define ll long long
using namespace std;
const int maxn=5e5+101;
const int MOD=1e9+7;
const int inf=2147483647;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
ll m,n,dp[101];
struct wzq{ll a[101][101];}T;
void init(){
for(int i=0;i<(1<<m);i++){
for(int j=0;j<(1<<m);j++){
if(i!=(1<<m)-1){
if((i|j)==(1<<m)-1)T.a[i][j]=1;
}
else if(j!=(1<<m)-1)T.a[i][j]=1;
}
}
}
wzq dot(wzq x,wzq y){
wzq sum;
for(int i=0;i<(1<<m);i++){
for(int j=0;j<(1<<m);j++){
sum.a[i][j]=0;
for(int k=0;k<(1<<m);k++){
(sum.a[i][j]+=x.a[i][k]*y.a[k][j]%MOD)%=MOD;
}
}
}
return sum;
}
wzq qpow(wzq x,ll y){
wzq sum;
for(int i=0;i<(1<<m);i++){
for(int j=0;j<(1<<m);j++)sum.a[i][j]=(i==j);
}
while(y){
if(y&1)sum=dot(sum,x);
y>>=1;x=dot(x,x);
}
return sum;
}
int main(){
m=read();n=read();init();
for(int i=0;i<(1<<m);i++)dp[i]=1;
T=qpow(T,n-1);
ll ans=0;
for(int i=0;i<(1<<m);i++){
for(int j=0;j<(1<<m);j++)(ans+=T.a[i][j]*dp[j]%MOD)%=MOD;
}
printf("%lld",(ans%MOD+MOD)%MOD);
return 0;
}