「BZOJ 1297」「SCOI 2009」迷路「矩陣乘法」
阿新 • • 發佈:2019-02-10
per 題解 algo code namespace sin 有向圖 line [1]
題意
邊權\(w \in [1, 9]\)的\(n\)個結點的有向圖,圖上從\(1\)到\(n\)長度為\(d\)的路徑計數,\(n \leq 10\).
題解
如果邊權為\(1\)很經典,設\(f[k][i]\)表示從\(1\)到\(i\),長度為\(k\)的路徑條數,則\(f[k][i] = \sum_{j=1}^n f[k - 1][j] a[j][i]\)。於是可以構造初始矩陣,再乘以\(a^k\)(\(a\)為圖的鄰接矩陣)。
現在邊權不唯一,但是邊權很小,可以拆點,一個點拆成\(9\)個點,\(9\)點連成一條鏈,如果要連出邊就從最後一個點連出去,如果連入邊就連到相應到點就好。
#include <algorithm> #include <cstdio> using namespace std; const int N = 12; const int M = 92; const int mo = 2009; struct matrix { int a[M][M], n, m; matrix operator *(const matrix & b) { matrix ans; ans.n = n; ans.m = b.m; for(int i = 1; i <= ans.n; i ++) { for(int j = 1; j <= ans.m; j ++) { ans.a[i][j] = 0; for(int k = 1; k <= m; k ++) { (ans.a[i][j] += a[i][k] * b.a[k][j]) %= mo; } } } return ans; } friend matrix mpow(matrix a, int b) { matrix ans = a; b --; for(; b >= 1; b >>= 1, a = a * a) if(b & 1) ans = ans * a; return ans; } } fir, tr; int n, m, d; char s[N]; int pos(int x, int y = 8) { return x + y * n; } int main() { scanf("%d%d", &n, &d); tr.n = tr.m = m = n * 9; for(int i = 1; i <= m; i ++) for(int j = 1; j <= m; j ++) tr.a[i][j] = 0; for(int i = 1; i <= n; i ++) { for(int j = 1; j <= 8; j ++) tr.a[pos(i, j - 1)][pos(i, j)] = 1; scanf("%s", s + 1); for(int j = 1; j <= n; j ++) { int w = s[j] ^ '0'; if(w) tr.a[pos(i)][pos(j, 9 - w)] = 1; } } fir.n = 1; fir.m = m; for(int i = 1; i <= fir.m; i ++) fir.a[1][i] = i == pos(1) ? 1 : 0; fir = fir * mpow(tr, d); printf("%d\n", fir.a[1][pos(n)]); return 0; }
「BZOJ 1297」「SCOI 2009」迷路「矩陣乘法」