题解【P5004 专心OI - 跳房子】

2022/9/15 23:20:13

本文主要是介绍题解【P5004 专心OI - 跳房子】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

本题的找规律题解到此为止。

为防止新人受到误导,不再接受新的此类题解。

以前的保留不会删除,但请不要再提交。


题目传送门。

矩阵加速模板题吧。给一个正经的不用找规律的做法。

考虑设 \(F_n\) 表示前 \(n\) 个格子的答案,\(f_n\) 表示最后降落在 \(n\) 的方案数,显然有 \(F_n=F_{n-1}+f_n\),由于 \(f_n\) 也是由一个 \(f\) 的前缀和得来,所以得到 \(F_{n}=F_{n-1}+F_{n-m-1}\)。

这个长得就很矩阵加速了。观察数据范围发现 \(m\) 很小,而这又是一个前缀和的形式,启示我们把 \(\{F_n,F_{n-1},\cdots,F_{n-m}\}\) 都放入一个矩阵里进行转移。

考虑用原矩阵 \(\begin{bmatrix} F_{n-1}\\ F_{n-2}\\ \cdots\\ F_{n-m-1} \end{bmatrix}\) 推出目标矩阵 \(\begin{bmatrix} F_{n}\\ F_{n-1}\\ \cdots\\ F_{n-m} \end{bmatrix}\)

对于 \(i\) 满足 \(n-m\le i\le n-1\),\(F_i\) 都是原矩阵已知的元素,不用管它。

对于 \(F_n\),把它直接替换成 \(F_{n-1}+F_{n-m-1}\),这两项恰好也是我们原矩阵已知的元素。

下面构造转移矩阵。

第一行是特殊的,因为它的转移是对于 \(F_n\) 的。我们将第一个设成 \(1\),最后一个设成 \(1\),其余设成 \(0\),表示对应 \(F_{n-1}\) 和 \(F_{n-m-1}\)。

然后剩下的那些行,只需要将 \(F_i\) 对应的位置设成 \(1\) 就可以,直接继承过来。

这样就得到了这样一个矩阵(这是当 \(m=5\) 时矩阵的样子,来类比一下,应该很容易能看出来):

\[\begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 \end{bmatrix}\]

这样的话,可以构造一个 \(\left(m+1\right)\times \left(m+1\right)\) 的转移矩阵然后快速幂即可。

矩阵乘法 \(\Theta(m^3)\),快速幂 \(\Theta(\log n)\),最终复杂度 \(\Theta(m^3\log n)\)。

代码:

const int MOD(1e9+7);

inline int add(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
inline int mul(int x,int y){return 1ll*x*y%MOD;}

ll n;
int m;

struct Matrix
{
	int G[30][30];
	Matrix(){rep(i,1,m) rep(j,1,m) G[i][j]=0;return;}
	Matrix operator*(const Matrix&x)
	{
		Matrix res;
		rep(i,1,m) rep(j,1,m) rep(k,1,m) res.G[i][j]=add(res.G[i][j],mul(G[i][k],x.G[k][j]));
		return res;
	}
};

inline Matrix ksm(ll y)
{
	Matrix ans,x;
	rep(i,1,m) ans.G[i][i]=1;
	rep(i,2,m) x.G[i][i-1]=1;
	x.G[1][1]=1,x.G[1][m]=1;
	while(y)
	{
		if(y&1) ans=ans*x;
		x=x*x;
		y>>=1;
	}
	return ans;
}

int main()
{
	scanf("%lld%d",&n,&m);
	++m;
	Matrix now=ksm(n+m-1);
	printf("%d\n",now.G[1][1]);
	return 0;
}


这篇关于题解【P5004 专心OI - 跳房子】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程