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

题意

边权(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];
                ans.a[i][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
", fir.a[1][pos(n)]);
    return 0;
}
原文地址:https://www.cnblogs.com/hongzy/p/10358921.html