标签:i++ span 预处理 组合数学 lse += nal 多少 快速
给你一张图,你刚开始在1号节点,每次你可以走到相邻的节点,每条边有一个边权,代表需要花费的时间。问有多少种方案刚好t时间走到n号节点。答案对2009取模。
对于 \(30\%\) 的数据,保证 \(n \leq 5\),\(t \leq 30\)。
对于 \(100\%\) 的数据,保证 \(2 \leq n \leq 10\),\(1 \leq t \leq 10^9\)。
先想一想\(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[ \begin{matrix} dp[m-1][1] & dp[m-1][2] & ... & dp[m-1][n] & ... & dp[m-9][1] & ... & dp[m-9][n] \end{matrix} \right]\)。
如果存在一条\(v->u\)的边且边权为\(val(v,u)\)。则令转移矩阵的第\(n \times (val(v,u)-1)+v\)行第\(u\)列设为1即可。
而为了保存m-1到m-8作为下一步转移的需要,我们还要做一步预处理。
//迷路
#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;
}
标签:i++ span 预处理 组合数学 lse += nal 多少 快速
原文地址:https://www.cnblogs.com/zcr-blog/p/13191548.html