题目描述 小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15)。他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M<=5,M<=N)个花圃中有不超过K(1<=K<M)个C形的花圃,其余花圃均为P形的花圃。 例如,N=10,M=5,K=3。则 CCPCPPPPCC 是一种不符合规则的花圃; CCPPPPCPCP 是一种符合规则的花圃。 请帮小L求出符合规则的花园种数Mod 1000000007 由于请您编写一个程序解决此题。 输入输出格式 输入格式: 一行,三个数N,M,K。 输出格式: 花园种数Mod 1000000007 输入输出样例 输入样例#1: 复制 【样例输入1】 10 5 3 【样例输入2】 6 2 1 输出样例#1: 复制 【样例输出1】 458 【样例输出2】 18 说明 【数据规模】 40%的数据中,N<=20; 60%的数据中,M=2; 80%的数据中,N<=10^5。 100%的数据中,N<=10^15。 分析:额,本来想刷数学题的,结果被这道题撞车了,实在好久没写过矩阵快速幂,于是各种qiao。首先80%不难想到状压dp,因为m小于5嘛,然后定义转移方程f[i][j],表示拿到第i个花时,后m个花的状态是j的方案数,现在考虑哪些状态可以转移,不难看出j状态可以由j>>2+1,j>>2+2^(m-1)+1(手动模拟一下吧),这两个状态转移过来于是打上标记con[i][j],于是可以写出一个n*2^(m-1)*2^(m-1)的转移方程,f[i][j]=f[i-1][k]+f[i][j]。这是80分的做法,现在n太大于是考虑优化,我们观察一下可以发现,后面的每种方案数其实就是前面的方案递推过去的,递推规则是不变的,且推了n次,然后手动玩了下样例,发现最后的方案数就等于最开始处理出来的con[i][j]的n次方,于是考虑矩阵快速幂(很裸的)。最后也是最玄学的地方,你可以玩个样例观察一下,发现答案就是矩阵的对角线和,至于为什么,可以这样考虑,我们最后算出的结果肯定是sigma(f[n+m][i]),而对角线上的点的值其实就对应每种状态i,于是考虑把它们加起来得到最优解。最后说一下,取模一定要勤快。
# include <iostream>
# include <cstdio>
# include <cmath>
# include <list>
# include <cstring>
# include <map>
# include <algorithm>
# include <queue>
using namespace std;
typedef long long ll;
ll read()
{
register ll f=1,i=0;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return f*i;
}
const int mod=1000000007;
ll con[40][40],res[40][40],n,m,k,tmp[40][40];
bool vis[40];
inline void MUL(ll s[][40],ll t[][40]){
memset(tmp,0,sizeof(tmp));
for(int i=0;i<(1<<m);++i)
for(int j=0;j<(1<<m);++j)
for(int k=0;k<(1<<m);++k)
tmp[i][j]=(tmp[i][j]+s[i][k]*t[k][j])%mod;
for(int i=0;i<(1<<m);++i)
for(int j=0;j<(1<<m);++j) t[i][j]=tmp[i][j];
}
inline void KUM(){
while(n){
if(n&1)MUL(con,res);
n>>=1;MUL(con,con);
}
}
int main(){
n=read(),m=read(),k=read();
for(int i=0;i<(1<<m);++i){
int x=i,sum=0;
while(x){if(x&1) sum++;x>>=1;}
if(sum<=k){
vis[i]=1;con[i>>1][i]=1;
con[(i>>1)+(1<<(m-1))][i]=1;
}
res[i][i]=1;
}
KUM();ll ans=0;
for(int i=0;i<(1<<m);i++)
if(vis[i]) ans+=res[i][i]%mod;
cout<<(ans%mod+mod)%mod<<endl;
}