[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]迷路——拆点+矩阵快速幂