花园 洛谷p1357

题目描述

小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。


前80%的N<=100000N<=100000,还是可做的,和容易想到用DP搞,因为m<=5m<=5,,所以我们可以把最后的m盆花进行状压,f[i][j]f[i][j]表示共有i盆花且最后mm盆花的状态为jj的时候的方案数,那么我们就可以将以个可以转移给jj的状态的方案书加给他,即状态转移方程为f[i][j] = Σ{f[i-1][k]}f[i][j]=Σf[i1][k],那么问题又来了,题目中说所有的花都是环形,我这样做如何保证所有的方案都是环形的呢.我们可以多次DP

每次DP前将其中一个合法状态赋值kk为1,然后从mm盆花一直更新到m+nm+n盆花,最后我再将f[m+n][k]f[m+n][k]加到ansans中,这样我就可以保证最后更新出的答案中的方案数都是有最开始的kk开始且又以kk结束的方案,即组成了一个环.最后的ansans即为我的方案数.

其实我们会发现,对于一个合法状态,更新它的合法状态始终是不变的,并且总会更新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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值