题目描述
小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:10 5 3
输出样例#1:458
输入样例#2:6 2 1
输出样例#2:18
说明
【数据规模】
40%的数据中,N<=20;
60%的数据中,M=2;
80%的数据中,N<=10^5。
100%的数据中,N<=10^15。
这题N有10^15这么大,而M又只有5,所以肯定是要用类似快速幂的东西来优化的。而矩阵快速幂是一种很常见的优化DP/递推的方式。所以这题的关键就是推出转移矩阵。
因为M只有5,所以我们先用DFS求出M内所有符合要求的花圃的情况。而对于每种情况,他所能转移到的情况只有两种,所以我们可以用一个邻接矩阵来存储所有的相互推出的状况。我们断环为链,再把1~M的部分接在N后面,那么这时要求的相当于就是从1~M情况开始,转移N次,则最后状态与1~M相同的方案数有多少种。
我们会发现i转移到j,可以看作i先转移到一个状态k,再由k转移到j,这是一个满足结合律的floyed类型的式子,可以用矩阵快速幂来搞。于是直接用快速幂转移N次,最后看有多少种f[i][i]即可。
#include<bits/stdc++.h>
#define MD 1000000007
#define MAXN 64
#define ll long long
using namespace std;
ll read(){
char c;ll x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';return x*y;
}
ll n,m,k,lim,ans,pre[6],ok[MAXN],A[MAXN][MAXN],B[MAXN][MAXN],U[MAXN][MAXN];
void work(ll re,ll num){
ok[re]=1;ll state=re>>1;
A[state][re]=1;
if(num==k&&!(re&1)) return;
A[pre[m]+state][re]=1;
}
void dfs(ll x,ll num,ll re){
if(x==m+1){work(re,num);return;}
dfs(x+1,num,re);
if(num<k) dfs(x+1,num+1,re|pre[x]);
}
void mul(ll a[][MAXN],ll b[][MAXN]){
register ll i,j,k;
for(i=0;i<=lim;i++)
for(j=0;j<=lim;j++){
ll res=0;
for(k=0;k<=lim;k++) res=(res+a[i][k]*b[k][j])%MD;
U[i][j]=res;
}
for(i=0;i<=lim;i++)
for(j=0;j<=lim;j++) a[i][j]=U[i][j];
}
void power(){
register ll i,j;
for(i=0;i<=lim;i++)
for(j=0;j<=lim;j++)
if(i==j) B[i][j]=1;else B[i][j]=0;
while(n){
if(n&1) mul(B,A);
mul(A,A);n>>=1;
}
}
int main()
{
pre[1]=1;for(ll i=2;i<=5;i++) pre[i]=pre[i-1]<<1;
n=read();m=read();k=read();
lim=(1<<m)-1;dfs(1,0,0);power();
for(ll i=0;i<=lim;i++) if(ok[i]) ans+=B[i][i],ans%=MD;
printf("%lld",ans);
return 0;
}