CF1117D Magic Gems 矩阵快速幂 DP

原题链接:CF1117D Magic Gems

题目大意

一开始有无穷多个牛逼石头,每个牛逼石头可以分解成(m)个普通石头,而普通石头不能再次分解出普通石头.你有一个大小为(n)的背包,牛逼石头和普通石头的大小都是1个单位,问恰好填满整个背包的方案数有多少.答案对(10^9+7)取模

此处讨论的方案数包含牛逼石头位置不同,但是数量相同的方案,即不认为数量相同的位置不同的属于同构的方案.

数据范围:

(1 leq n leq 10^{18})

(2 leq m leq 100)

思路

看到(n)大的离谱而(m)很小,支持(O(m^3logn))很可能就是一个矩阵快速幂了,顺着可以先把递推表达式搞出来.

这里有一个比较浅显的思路就是直接(dp)(f[i][j])表示当前一共有(i)个石头(j)个位置的方案数,不过这样去做有很多组合数上的问题不太好搞,有个更好的做法是:(f[i])表示占用(i)个单位的方案数.那么转移也比较显然,就是枚举这个状态的最后一部分石头是怎么来的:要么从前一个状态加了一个不分解的牛逼石头,也就是(f[i] += f[i - 1]),要么把一个牛逼石头分解了再加进来,也就是(f[i] += f[i - m])合起来就是(f[i] = f[i - 1] + f[i - m]).这一步比较显然,不是这个题的重点.

考虑优化,直接暴力(dp)显然是(O(nm))的,需要矩阵快速幂:

  • 状态矩阵(A = [f[0],f[1],f[2],...f[m-1]]),状态矩阵是一个(1*m)的行向量.
  • 转移矩阵(B).我们希望能有这样的一个结果:([f[0],f[1],f[2],...f[m-1]] *B = [f[1],f[2],f[3]...f[m]]).那么可以注意到除了最后一列元素(f[m-1])比较怪之外,其他的元素其实只是后面的往前面挪动了一位,对此可以先构造转移矩阵(B)的前(m-1)列,对于这些列,根据矩乘的式子可以找到就是让第(i)列的(i+1)行之外的所有元素为(0)而他系数是(1)就可以了.对于最后一列元素直接单独对他做就可以了:(f[m]=f[0] + f[m-1]),只需要这一列本来的元素以及最左侧的(f[0])就可以了,系数就是(B[1][m] = 1)以及(B[m][m]=1).
  • 考虑转移:首先(A*B)的结果会使得(A[1][1])的位置变成(f[1]),所以乘上(B^i)之后,(A[1][1]=f[i]).那么结果需要的是(f[n])直接让(A*B^n)即可.后者可以矩阵快速幂解决.

最后答案取(A[1][1])输出即可.

(由于矩乘部分也是copy别人的代码来的,不知道会不会有锅,有锅别找我(逃))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 105,MOD = 1e9+7;
ll n,m;
struct Matrix
{
	ll a[N+5][N+5];
	ll* operator[](int x){return a[x];}
	Matrix operator*(Matrix& rhs){
		Matrix res;
		for(int i=1;i<=m;++i){
			for(int j=1;j<=m;++j){
				for(int k=1;k<=m;++k)
				{
					res[i][j] = (res[i][j] + (ll)a[i][k] * rhs[k][j] % MOD) % MOD;
				}
			}
		}
		return res;
	}
	Matrix()	{memset(a,0,sizeof(a));}
};
Matrix mat_pow(Matrix x,ll i)
{
	Matrix y;
	for(int j=1;j<=m;++j)y[j][j]=1;
	while(i)
	{
		if(i&1LL)y=y*x;
		x=x*x;
		i>>=1;
	}
	return y;
}

int main()
{
	scanf("%lld%lld",&n,&m);
	Matrix A,B;
	forn(i,1,m)	A.a[1][i] = 1;
	forn(i,1,m - 1)	B.a[i + 1][i] = 1;
	B.a[1][m] = 1;B[m][m] = 1;
	
	B = mat_pow(B,n);
	A = A * B;
	printf("%lld",A[1][1]);
	
    return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/14315067.html