其他分享
首页 > 其他分享> > 「 洛谷 」P2768 珍珠项链

「 洛谷 」P2768 珍珠项链

作者:互联网

转:

「 洛谷 」P2768 珍珠项链

珍珠项链

题目限制

题目知识点

题目来源

「 洛谷 」P2768 珍珠项链


为了方便大家阅读通畅,题目可能略有改动,保证不会造成影响

题目

题目背景

小(L) 通过泥萌的帮助,成功解决了牛栏的修建问题
奶牛们觉得主人非常厉害,就再也不偷懒了:母牛们奋力挤奶、生娃
小(L) 也因此成为了一代富豪

但是一直困扰 小(L) 的就是单身问题
小(L) 经过长久的寻觅,小(L) 终于找到了一个心仪的漂亮妹子
于是,小(L) 打算在 (5.20) 那天给妹子一个惊喜

题目描述

小(L) 决定用 (K) 种珍珠为妹子做一串举世无双的珍珠垂饰
珍珠垂饰是由珍珠连接而成的,其长度可以认为就是珍珠垂饰上珍珠的个数

小(L) 现在腰缠万贯,每种珍珠他都拥有 (N) 颗
根据将珍珠垂饰打开后珍珠不同的排列顺序可以区别出不同种类的项链
现在,小(L) 好奇自己可以组成多少种长度为 (1) 至 (N) 且不同的珍珠垂饰
当然,为显富有,每串珍珠垂饰都要必须由 (K) 种珍珠连接而成
由于答案可能太大,只需要取模 1234567891

这一定难不倒聪明的你吧
如果你能帮 小(L) 解决这个问题,他会把最后的资产分给你一半哦

格式

输入格式

第一行输入一个整数 (T) ,表示测试数据的个数

输出格式

样例

样例输入

2
2 1
3 2

样例输出

2
8

提示

时间限制

对于 (70% sim 100%) 的数据:时间限制 (10ms)

数据范围

对于 (40%) 的数据:(1<= N<= 1e5),(0 leq K leq 30)
对于 (100%) 的数据:(T leq 10),(1 leq N leq 1e9),(0 leq K leq 30)


思路

题目所说的 珍珠项链长度为 (i) 的种类数 其实就是 用不同的方式连接出长度为 (i) 的珍珠项链方案数
我们可以先思考 (dp):(dp[i][j]) 表示 用 (j) 种珍珠 连接出 长度为 (i) 的珍珠项链的方案数

我们可以进一步推出 (dp) 的状态转移方程:(dp[i][j] = dp[i - 1][j - 1] * (K - (j - 1)) + dp[i - 1][j] * j)

最后的答案就是 (dp[1][K] + dp[2][K] + dp[3][K] + ··· + dp[N][K] = sum_{i = 1}^{N} dp[i][K])


分析

如果这道题的数据较小的话,用 (dp) 就可以 (AC) 了,可惜 (N) 的范围太大了
这时候我们就要思考如何优化 (dp) 的时间复杂度了

(dp) 是在进行递推转移,不妨可以把 (dp) 放在矩阵里求解
我们设 (ans[i] = dp[1][K] + dp[2][K] + ··· + dp[i][K]),我们所求的答案就是 (ans[N])

我们可以构造一个 原始矩阵

[begin{bmatrix} & dp[1][1] & dp[1][2] & dp[1][3] & ··· & dp[1][K] & ans[0] & \ end{bmatrix} ]

为了状态转移,我们需要将它变成:

[begin{bmatrix} & dp[2][1] & dp[2][2] & dp[2][3] & ··· & dp[2][K] & ans[1] & \ end{bmatrix} ]

继续进化成:

[begin{bmatrix} & dp[3][1] & dp[3][2] & dp[3][3] & ··· & dp[3][K] & ans[2] & \ end{bmatrix} ]

以此类推,直到求出 (ans[N]) 为止

构造的原始矩阵(dp) 的状态转移方程 中,我们可以推出 加速矩阵 ((k) 行 (k) 列)

[begin{bmatrix} & 1 & k - 1 & 0 &0 & ··· & 0 & 0 & \ & 0 & 2 & k - 2 &0 & ··· & 0 & 0 & \ & 0 & 0 & 3 & k - 3 & ··· & 0 & 0 & \ & ··· & ··· & ··· & ··· & ··· & 0 & 0 & \ & ··· & ··· & ··· & ··· & ··· & 1 & 0 & \ & 0 &0 &0 &0 &0 &k &1 & \ & 0 &0 &0 &0 &0 &0 &1 & \ end{bmatrix} ]

刚开始是 (ans[0]),答案是 (ans[N]),所以要乘以 (N) 个加速矩阵,可是仍然要超时

这时候就需要矩阵快速幂了:根据矩阵乘法的结合律,先把 (N) 个加速矩阵乘起来,再用 原始矩阵 乘以 这个得到的矩阵,就可以得到最终的答案了
(注意:矩阵1 (*) 矩阵2 不一定等于 矩阵2 (*) 矩阵1,所以不能乘反了)


代码

#include 
#include 

const long long MOD = 1234567891LL;
int T, N, K;

const int MAXK = 30;
struct Matrix
{
	long long Mat[MAXK + 5][MAXK + 5];
	int R, C;
	
	Matrix()
	{
		for (int i = 1; i <= MAXK + 1; i++)
			for (int j = 1; j <= MAXK + 1; j++)
				Mat[i][j] = 0LL;
	}
	
	void Read() const // 矩阵的输入 
	{
		if (R < 1 || C < 1) return ;
		for (int i = 1; i <= R; i++)
			for (int j = 1; j <= C; j++)
				scanf("%lld", &Mat[i][j]);
	}
	
	void Write() const // 矩阵的输出 
	{
		if (R < 1 || C < 1) return ;
		for (int i = 1; i <= R; i++) {
			for (int j = 1; j < C; i++)
				printf("%lld ", Mat[i][j]);
			printf("%lldn", Mat[i][C]);
		}
	}
	
	Matrix operator * (const Matrix One) const // 重载矩阵的乘号 
	{
		Matrix Res;
		Res.R = R, Res.C = C;
		for (int i = 1; i <= Res.R; i++) 
			for (int j = 1; j <= Res.C; j++)
				for (int k = 1; k <= One.R; k++)
					Res.Mat[i][j] = (Res.Mat[i][j] + Mat[i][k] * One.Mat[k][j]) % MOD;
		return Res;
	}
}A, B;

Matrix Pow(Matrix One, long long k) // 矩阵快速幂 
{
	Matrix Res, cnt = One;
	Res.R = K + 1, Res.C = K + 1;
	for (int i = 1; i <= K + 1; i++)
		Res.Mat[i][i] = 1LL; // 单位矩阵 
	for (int i = k; i >= 1; i >>= 1)
	{
		if (i & 1) Res = Res * cnt;
		cnt = cnt * cnt;
	}
	return Res;
}

void Init(int k) // 初始化 原始矩阵 和 加速矩阵 
{
	A.R = 1, A.C = k + 1;
	A.Mat[1][1] = (long long)k;
	
	B.R = B.C = k + 1;
	B.Mat[1][1] = 1LL;
	for (int i = 2; i <= k; i++)
	{
		B.Mat[i][i] = (long long)i;
		B.Mat[i - 1][i] = (long long)k + 1LL - i;
	}
	B.Mat[k][k + 1] = B.Mat[k + 1][k + 1] = 1LL;
}

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d", &N, &K);
		Init(K);
		Matrix ans = A * Pow(B, N);
		printf("%lldn", ans.Mat[1][K + 1]);
	}
	return 0;
}


转:

「 洛谷 」P2768 珍珠项链

标签:洛谷,珍珠,矩阵,P2768,珍珠项链,bmatrix,ans,dp
来源: https://www.cnblogs.com/wangtcc/p/14541188.html