LUOGU P1357 花园
题目描述
小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15)。他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M<=5,M<=N)个花圃中有不超过K(1<=K
解题思路
首先考虑80分做法:可以用dp,dp[i][S] 表示到了第i位,这m位的状态为S,这样的转移比较好写。再考虑满分做法,看到数据范围应该能猜出是矩阵乘法加速dp,因为局面中的任何一个状态都在 1<
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int mod = 1e9+7;
typedef long long LL;
LL n,ans;
int m,K;
bool ok[1<<6];
struct Mat{
LL x[1<<6][1<<6];
Mat(){memset(x,0,sizeof(x));}
Mat operator *(const Mat &h){
Mat c;
for(register int i=0;i<1<<m;i++)
for(register int j=0;j<1<<m;j++)
for(register int k=0;k<1<<m;k++)
c.x[i][j]=(c.x[i][j]+x[i][k]*h.x[k][j])%mod;
return c;
}
}pre,a,b;
inline void fast_pow(Mat A,LL t){
for(;t;t>>=1){
if(t&1) b=b*A;
A=A*A;
}
}
int main(){
scanf("%lld%d%d",&n,&m,&K);
for(register int i=0;i<1<<m;i++){
int tmp=i,cnt=0;
while(tmp) {
if(tmp&1) cnt++;
tmp>>=1;
}
if(cnt<=K) {
ok[i]=1;
a.x[i>>1][i]=1;
if((i&1)==1 || cnt<K) a.x[(i>>1)|(1<<m-1)][i]=1;
}
}
for(register int i=0;i<1<<m;i++) b.x[i][i]=1;
fast_pow(a,n);
for(register int i=0;i<1<<m;i++)
if(ok[i]) ans=(ans+b.x[i][i])%mod;
cout<<ans<<endl;
return 0;
}