HDU 2157 [How many ways??]

解题思路

我们很愉快地发现了这题可以离线。然后我们就尝试预处理所有时间所有点对间的方案数。我们将其维护在(F)里。(F[i][j][k])表示从(i)(j),经过(k)个点的方案数。

当我们从(k)(k+1)转移的时候,我们枚举点(u)(v),如果(u)(v)有一条边,那么我们将(F[i][v][k+1])加上(F[i][u][k])即可。直接做的做法在(O(kn^3)),完全没问题。

如果我们将边的关系建成邻接表,有边为(1),没边为(0),那么就可以写成矩阵乘法的样子。嗯,就像参考程序里那样。

参考程序

#include <bits/stdc++.h>
using namespace std;

const int Mod = 1000;
int n, m, A[ 30 ][ 30 ], F[ 30 ][ 30 ][ 30 ], T;

void Work() {
    memset( A, 0, sizeof( A ) );
    memset( F, 0, sizeof( F ) );
    for( int i = 0; i < n; ++i ) F[ i ][ i ][ 0 ] = 1;
    for( int i = 0; i < m; ++i ) {
        int x, y; scanf( "%d%d", &x, &y );
        A[ x ][ y ] = 1;
    }
    for( int times = 1; times < 20; ++times )
        for( int i = 0; i < n; ++i )
            for( int j = 0; j < n; ++j )
                for( int k = 0; k < n; ++k ) 
                    F[ i ][ j ][ times ] = ( F[ i ][ j ][ times ] + A[ i ][ k ] * F[ k ][ j ][ times - 1 ] % Mod ) % Mod;
    int T; scanf( "%d", &T );
    for( int i = 1; i <= T; ++i ) {
        int x, y, k; scanf( "%d%d%d", &x, &y, &k );
        printf( "%d
", F[ x ][ y ][ k ] );
    }
    return;
}

int main() {
    scanf( "%d%d", &n, &m );
    while( !( n == 0 && m == 0 ) ) {
        Work();
        scanf( "%d%d", &n, &m );
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chy-2003/p/9774227.html