[SCOI2009] 迷路(dp+矩阵快速幂)

Posted zcr-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2009] 迷路(dp+矩阵快速幂)相关的知识,希望对你有一定的参考价值。

题目大意

给你一张图,你刚开始在1号节点,每次你可以走到相邻的节点,每条边有一个边权,代表需要花费的时间。问有多少种方案刚好t时间走到n号节点。答案对2009取模。

对于 (30\%) 的数据,保证 (n leq 5)(t leq 30)

对于 (100\%) 的数据,保证 (2 leq n leq 10)(1 leq t leq 10^9)

Analysis

先想一想(30\%)的数据怎么做?看到计数问题多半是组合数学或dp。而这题组合数学应该不太好做所以我们考虑dp。

设经过(i)步到达节点(u)的方案数为(dp[i][u])。那所有连向u的边都可以被用来扩展,所以(dp[i][u]=sum_{(v,u) in E} dp[i-val(v,u)][v])(val(v,u))是边权。

这样做应该是(O(n^2t))的(菊花图)。

怎么优化呢?如果边权只有可能是1的话,就有(dp[i][u]=sum_{(v,u) in E} dp[i-1][v])。i的dp方程只与i-1有关,所以可以矩阵快速幂优化。

注意到边权只有可能是1-9,那么我们也可以用一个稍微大一点的矩阵来存。

具体来讲假设我们现在要求的是m时刻。

那我们的答案向量可以是(left[ egin{matrix} dp[m-1][1] & dp[m-1][2] & ... & dp[m-1][n] & ... & dp[m-9][1] & ... & dp[m-9][n] end{matrix} ight])

如果存在一条(v->u)的边且边权为(val(v,u))。则令转移矩阵的第(n imes (val(v,u)-1)+v)行第(u)列设为1即可。

而为了保存m-1到m-8作为下一步转移的需要,我们还要做一步预处理。

Code

//迷路 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Matrix{
	int arr[100][100];
	int size;
}g, dp;
int n, t;
char s[11];
int sy[11][11];
inline void init(Matrix &ret, int size) {
	ret.size = size;
	for (int i = 1; i <= size; ++i) {
		for (int j = 1; j <= size; ++j) {
			ret.arr[i][j] = 0;
		}
	}
}
inline Matrix Mul(Matrix A, Matrix B) {
	Matrix C;
	init(C, A.size);
	for (int i = 1; i <= A.size; ++i) {
		for (int k = 1; k <= A.size; ++k) {
			if (A.arr[i][k] == 0) continue;
			for (int j = 1; j <= A.size; ++j) {
				C.arr[i][j] = (C.arr[i][j] + A.arr[i][k] * B.arr[k][j]) % 2009;
			}
		}
	}
	return C;
}
inline Matrix ksm(Matrix A, int b) {
	Matrix ret;
	init(ret, A.size);
	for (int i = 1; i <= ret.size; ++i) ret.arr[i][i] = 1;
	while (b) {
		if (b & 1) ret = Mul(ret, A);
		A = Mul(A, A);
		b >>= 1;
	}
	return ret;
}
int main() {
	scanf("%d%d", &n, &t);
	init(g, n * 9);
	for (int i = 1; i <= n; i++) {
		scanf("%s", s + 1);
		for (int j = 1; j <= n; j++) {
			int k = s[j] - ‘0‘;
			sy[i][j] = k; 
			if (k) {
				g.arr[(9 - k) * n + i][n * 8 + j] = 1;
			}
		}
	}
	for (int i = n * 9; i > n; i--) {
		g.arr[i][i - n] = 1;
	}
	init(dp, n * 9);
	dp.arr[1][1] = 1;
	for (int i = 1; i <= min(t, 8); i++) {
		for (int j = 1; j <= n; j++) {
			for (int k = 1; k <= n; k++) {
				if (sy[j][k] && i >= sy[j][k]) {
					dp.arr[1][i * n + k] += dp.arr[1][(i - sy[j][k]) * n + j];
				}
			}
		}
	}
	if (t <= 8) {
		printf("%d", dp.arr[1][t * n + n]);
	} else {
		dp = Mul(dp, ksm(g, t - 8));
		printf("%d", dp.arr[1][n * 9]);
	}
	return 0;
}

以上是关于[SCOI2009] 迷路(dp+矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1297 [SCOI2009]迷路——拆点+矩阵快速幂

LUOGU P4159 [SCOI2009]迷路(矩阵乘法)

BZOJ1297 SCOI2009 迷路 矩阵乘法

[luogu4159 SCOI2009] 迷路(矩阵乘法)

SCOI2009 迷路

bzoj1297 / P4159 [SCOI2009]迷路