1. 程式人生 > 實用技巧 >BZOJ-1297 [SCOI2009]迷路(鄰接矩陣的冪)

BZOJ-1297 [SCOI2009]迷路(鄰接矩陣的冪)

題目描述

  有向圖有 \(n(2\leq n\leq 10)\) 個節點,從節點 \(0\) 出發,必須恰好在 \(t(1\leq t\leq 10^9)\) 時刻到達節點 \(n-1\)。 現在給出該有向圖(邊權 \(1\) ~ \(9\)),求總共有多少種不同的路徑。

分析

  首先有一個離散數學中的結論:用鄰接矩陣 \(A\) 存無向圖頂點間的關係,則 \(A^n\) 中的 \(a_{ij}\) 代表點 \(i\) 恰好走 \(n\) 步能到達點 \(j\) 的方案數。

  由於邊權範圍為 \(1\) ~ \(9\),不能直接矩陣求冪,所以考慮如何建圖:

  \(1.\) 把原圖中的點拆成 \(9\)

個點:如果原圖中的點為 \(x\),則拆後的點為 \((x-1)\times 1+i(1\leq i\leq 9)\),點 \(x\)關鍵點\((x-1)\times 9+1\),並把點 \((x-1)\times 9+i(1\leq i\leq 8)\) 向點 \((x-1)\times 9+i+1(1\leq i\leq 8)\) 連邊。

  \(2.\) 原圖中的點之間連邊:假設原圖中點 \(x\) 與點 \(y\) 的邊權為 \(w\),則連線新圖中的點 \((x-1)\times 9+w\) 和點 \((y-1)\times 9+1\)

  假設原圖的鄰接矩陣為:\(\begin{bmatrix}2&1\\2&0\end{bmatrix}\)

  則新圖為:

  這樣從點 \(x\) 必須走 \(k\) 條邊才能走到點 \(y\),這時矩陣求冪即可。起點為 \(1\),終點為 \((n-1)\times 9+1\)

程式碼

#include<bits/stdc++.h>
using namespace std;
int n,t;
const int mod=2009;
struct matrix
{
    int mat[310][310];
    matrix()
    {
        memset(mat, 0, sizeof(mat));
    }
}A;
matrix mul(matrix A,matrix B)
{
    matrix ans;
    for(int i=1;i<=9*n;i++)
        for(int j=1;j<=9*n;j++)
            for(int k=1;k<=9*n;k++)
                ans.mat[i][j]=(ans.mat[i][j]+A.mat[i][k]*B.mat[k][j])%mod;
    return ans;
}
matrix matrix_pow(matrix a,int b)
{
    matrix ans;
    for(int i=1;i<=9*n;i++)
        for(int j=1;j<=9*n;j++)
            ans.mat[i][j]=(i==j);
    while(b)
    {
        if(b&1)
            ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
int a[310][310];
int main()
{
    cin>>n>>t;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=8;j++)
            A.mat[(i-1)*9+j][(i-1)*9+j+1]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%1d",&a[i][j]);
            if(a[i][j]>0)
                A.mat[(i-1)*9+a[i][j]][(j-1)*9+1]=1;
        }
    }
    cout<<matrix_pow(A,t).mat[1][(n-1)*9+1]<<endl;
    return 0;
}