1. 程式人生 > >「BZOJ 1297」「SCOI 2009」迷路「矩陣乘法」

「BZOJ 1297」「SCOI 2009」迷路「矩陣乘法」

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」迷路「矩陣乘法」