「P5004」专心OI - 跳房子 解题报告

题面

\(N\)个无色格子排成一行,选若干个格子染成黑色,要求每个黑色格子之间至少间隔\(M\)个格子,求方案数

思路:

矩阵加速

根据题面,这一题似乎可以用递推

设第\(i\)个格子的编号为\(i\),有\(i\)个格子时的方案数为\(f(i)\)

显然,当 \(i \le M+1\) 时,

可以所有格子不染色(方案数为\(1\)种,或者最多一个格子染色(方案数为\(i\)种)

所以有\(f(i)=i+1\)

\(i>M+1\)时,

对于第\(i\)个格子可以由第\(i-1\)个格子转移过来,

而第\(i\)个格子有两种情况

1、不染色,显然可以这种情况下方案数为\(f(i-1)\)

2、染色,可以看出第\([i-m,i-1]\)个格子必定不染色,也就是没有贡献的,方案数为\(f(i-m-1)\)

但是!

\(N \le 10^{18}\)\(M \le 15\)

可以使用矩阵加速递推

求解

我们要记录的是应该是\(f(i) \to f(i+m)\)一共\(m+1\)个元素,于是就用一个\((M+1)^2\)的矩阵进行加速,配合快速幂求解

Code:

#include<bits/stdc++.h>
#define ll long long
#define Mod 1000000007
#define N 20
using namespace std;
int n;
ll b;
struct node{//矩阵放结构体里
	ll f[N][N];
}res,a;
node operator* (const node a,const node b)//重载*运算
{
	int i,j,k;
	node c;ll res;
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
		{
			res=0;
			for(k=0;k<n;k++)
				res=(res+a.f[i][k]*b.f[k][j])%Mod;
			c.f[i][j]=res;
		}
	return c;
}
void init(){//初始化
	int i;
	for(i=0;i<n;i++) res.f[0][i]=i+2;//矩阵下标从0开始,所以+2
	for(i=0;i<n-1;i++) a.f[i+1][i]=1;
	a.f[n-1][n-1]=a.f[0][n-1]=1;
}
void quickPow(ll b)
{
	while(b)
	{
		if(b&1) res=res*a;
		b>>=1;a=a*a;
	}
}
int main()
{
    scanf("%lld%d",&b,&n);--b,++n;//res初始为b=1的情况,所以实际的b要-1
    init();quickPow(b);//n++是方便计算间隔
    printf("%lld",res.f[0][0]);
    return 0;
}
posted @ 2019-02-21 21:09  h^ovny  阅读(272)  评论(0编辑  收藏  举报