题目描述
小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
输入输出样例
说明
【数据规模】
40%的数据中,N<=20;
60%的数据中,M=2;
80%的数据中,N<=10^5。
100%的数据中,N<=10^15。
前80%的N<=100000,还是可做的,和容易想到用DP搞,因为m<=5,,所以我们可以把最后的m盆花进行状压,f[i][j]表示共有i盆花且最后m盆花的状态为j的时候的方案数,那么我们就可以将以个可以转移给j的状态的方案书加给他,即状态转移方程为f[i][j]=Σf[i−1][k],那么问题又来了,题目中说所有的花都是环形,我这样做如何保证所有的方案都是环形的呢.我们可以多次DP
每次DP前将其中一个合法状态赋值k为1,然后从m盆花一直更新到m+n盆花,最后我再将f[m+n][k]加到ans中,这样我就可以保证最后更新出的答案中的方案数都是有最开始的k开始且又以k结束的方案,即组成了一个环.最后的ans即为我的方案数.
其实我们会发现,对于一个合法状态,更新它的合法状态始终是不变的,并且总会更新N次,也就是说对于每一次转移,都相当于给原数组乘以了一个矩阵,并且很显然这个矩阵是不变的,而且它就是当初我们处理出来的表示转移关系的那个bool数组,既然这样,那我们的$f[i][j]中的第一维就没有任何意义了,我们可以将它扔掉,变成一个一维数组,然后我们又会发现,其实最终的答案就是那个矩阵的对角线之和。
80分:
#include<iostream>
#include<cstring>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MOD=1000000007;
long long n;
int m,K,S,sta[100],v[100][100];
int f[100010][64],ans=0;
inline Matrix Pow(Matrix x,int k)
{
Matrix ans=x;
k--;
for(;k;k>>=1,x=x*x){
if(k&1){
ans=ans*x;
}
}
return ans;
}
inline bool pd(int s)
{
int i,num=0;
f(i,0,m-1){
if(s&(1<<i)) num++;
if(num>K) return false;
}
return true;
}
inline bool check(int x,int y)
{
int i;
f(i,0,m-2){
int a=(sta[x]>>i)&1;
int b=(sta[y]>>i+1)&1;
if(a!=b) return false;
}
return true;
}
inline void dp(int s)
{
int i,j,k;
memset(f,0,sizeof(f));
f[m][s]=1;
f(i,m+1,m+n){
f(j,1,sta[0]){
f(k,1,sta[0]){
f[i][j]=(f[i][j]+f[i-1][k]*v[k][j])%MOD;
}
}
}
ans=(ans+f[m+n][s])%MOD;
}
int main()
{
ios::sync_with_stdio(false);
int i,j;
cin>>n>>m>>K;
S=(1<<m)-1;
f(i,0,S){
int num=0;
if(pd(i)) sta[++sta[0]]=i;
}
f(i,1,sta[0]){
f(j,1,sta[0]){
if(check(i,j)){
A.v[i][j]=1;
}
}
}
f(i,1,sta[0]){
dp(i);
}
cout<<ans<<endl;
return 0;
}
100分:
#include<iostream>
#include<cstring>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MOD=1000000007;
long long n;
int m,K,S,sta[100];
int f[100010][64];
long long ans=0;
struct Matrix{
long long v[50][50];
Matrix(){
memset(v,0,sizeof(v));
}
Matrix operator *(const Matrix& x)const{
Matrix ans;
int i,j,k;
f(i,1,sta[0]){
f(j,1,sta[0]){
f(k,1,sta[0]){
ans.v[i][j]=(ans.v[i][j]+v[i][k]*x.v[k][j])%MOD;
}
}
}
return ans;
}
}A;
inline Matrix Pow(Matrix x,long long k)
{
Matrix ans=x;
k--;
for(;k;k>>=1,x=x*x){
if(k&1){
ans=ans*x;
}
}
return ans;
}
inline bool pd(int s)
{
int i,num=0;
f(i,0,m-1){
if(s&(1<<i)) num++;
if(num>K) return false;
}
return true;
}
inline bool check(int x,int y)
{
int i;
f(i,0,m-2){
int a=(sta[x]>>i)&1;
int b=(sta[y]>>i+1)&1;
if(a!=b) return false;
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
int i,j;
cin>>n>>m>>K;
S=(1<<m)-1;
f(i,0,S){
int num=0;
if(pd(i)) sta[++sta[0]]=i;
}
f(i,1,sta[0]){
f(j,1,sta[0]){
if(check(i,j)){
A.v[i][j]=1;
}
}
}
Matrix b=Pow(A,n);
f(i,1,sta[0]){
ans=(ans+b.v[i][i])%MOD;
}
cout<<ans<<endl;
return 0;
}