[C++][SCOI2009]迷路

[SCOI2009]迷路

題目描述

windy在有向圖中迷路了。 該有向圖有 N 個節點,windy從節點 0 出發,他必須恰好在 T 時刻到達節點 N-1。 現在給出該有向圖,你能告訴windy總共有多少種不同的路徑嗎? 注意:windy不能在某個節點逗留,且通過某有向邊的時間嚴格爲給定的時間。

輸入格式:

第一行包含兩個整數,N T。 接下來有 N 行,每行一個長度爲 N 的字符串。 第i行第j列爲’0’表示從節點i到節點j沒有邊。 爲’1’到’9’表示從節點i到節點j需要耗費的時間。

輸出格式:

包含一個整數,可能的路徑數,這個數可能很大,只需輸出這個數除以2009的餘數。

輸入樣例:

2 2
11
00

輸出樣例

#1:

1

輸入樣例:

5 30
12045
07105
47805
12024
12345

輸出樣例:

852

分析

(剛開始還以爲老師誤把圖論題放到矩陣加速這一板塊來了呢)
讀入雖然爲字符串,但我們將它轉換一下,不就是一個矩陣了嗎?
就以 樣例一 爲例,可得
(1100)\begin{pmatrix} 1 & 1 \\ 0 & 0\\ \end{pmatrix}
顯然這是鄰接矩陣,如果此矩陣中的(i,j)\begin{pmatrix} i,j\end{pmatrix} = 1 的話就表示 i 到 j 是連通的
然而,對於這種01矩陣我們發現有一個特殊的性質:我們用這個01矩陣乘自己的話,不正表示從 i 到 j 花費 T 時間的方案數嗎?
首先從(i,j)\begin{pmatrix} i,j\end{pmatrix}是有 xx 條路可走的,而從(j,k)\begin{pmatrix} j,k\end{pmatrix}yy 條路可走,那麼根據乘法原理,從(i,k)\begin{pmatrix} i,k\end{pmatrix}就有x×yx \times y個方案數到 k。
所以我們只需要一個qkpow即可求出答案
但是很尷尬的是,這僅限於 01矩陣,對於這個邊權可爲9的矩陣,真是一點辦法兒也沒有呢 ……
所以這時候我們需要運用“拆點”這一新奇的想法

拆點

拆點,對於此題而言,其實就是將一個點分最大權值個點,這是什麼意思呢?我們就假設有一個邊權 <= 2的2階方陣,如圖(以下圖片來源:GM的神奇PPT

畫出行程圖:
在這裏插入圖片描述
於是我們可以就可以將1、2號節點拆成兩個(畢竟此矩陣中最大權值爲2)
(1.12.11.22.2)\begin{pmatrix} 1.1 &amp; 2.1\\ 1.2 &amp; 2.2\\ \end{pmatrix}
但是我們還是以 1.1 和 2.1 表示原來的那個點,1.2 和 2.2 我們可以把它看做兩個虛點
然後我們就開始連邊
第一步我們需要把 1.1 和 1.2,2.1 和 2.2連起來(相當於一個點,自己都走不到自己要它也就沒什麼用了)


接着,就得開始把 1 和 2兩個節點連起來,此時我們要湊 2,只需要把1.2 和 2.1連起來,1.1 到 1.2就爲 2了呀
在這裏插入圖片描述
注意,有自環的也要連

小結代碼

在這裏插入圖片描述
不知你有沒有看不懂的絕望,反正我剛開始,是一臉懵呢
首先,我們從這個 gm 函數開始看
(1.12.13.1n.11.22.23.2n.21.32.33.3n.31.Maxn2.Maxn3.Maxnn.Maxn)\begin{pmatrix} 1.1 &amp; 2.1 &amp; 3.1 &amp; \cdots &amp; n.1 \\ 1.2 &amp; 2.2 &amp; 3.2 &amp; \cdots &amp; n.2 \\ 1.3 &amp; 2.3 &amp; 3.3 &amp; \cdots &amp; n.3 \\ \vdots &amp; \vdots &amp; \vdots &amp; \vdots &amp; \vdots \\ 1.Maxn &amp; 2.Maxn &amp; 3.Maxn &amp; \cdots &amp; n.Maxn \\ \end{pmatrix}
其實,(i1)Maxn+j(i - 1) * Maxn + jiijj合起來就可以看做一個節點,怎麼理解呢?
結合第一個 for 循環:就是將自己連起來(也就是1.1 連 1.2這種)
b[gmgm(i,j)][gmgm(i,j + 1)]結合一下上面的圖一下就理解了。
接下來就是gmgm函數了, i.ji.j這個節點的前一列的最後一個是第(i - 1)* Maxn點,那麼再加上一個 j,不就又相當於又會新開一列,且剛好會在第 j 個位置。
舉個栗子:
要找到2.32.3,我們假設有一個人在0號節點位置,它肯定得走 Maxn 個點才能到達1.Maxn,然後需要再走一個點才能達到2.1,最後他就僅僅只需要走兩個點便能到達2.3了,不是嗎?
((2 - 1) ×\times Maxn + 1 + (3 - 1) = (2 - 1) ×\times Maxn + 3)

代碼

#include<cstdio>
#include<cstring>
#define M 10
#define reg register
#define mod 2009
 
 
int n,T,Maxn;
int A[M + 5][M + 5];
 
struct Matrix{
    int c[M * M + 5][M * M + 5];
    int n,m;
    Matrix() { memset(c,0,sizeof(c)); }
    Matrix operator * (const Matrix & rhs){
        Matrix Ans;
        Ans.n = n,Ans.m = rhs.m;
        for (reg int i = 1;i <= Ans.n; ++ i)
            for (reg int j = 1;j <= Ans.m; ++ j)
                for (reg int k = 1;k <= m; ++ k)
                    Ans.c[i][j] = (Ans.c[i][j] + c[i][k] * rhs.c[k][j] % mod) % mod;
        return Ans;
    }
}B,G;
 
Matrix Qkpow(Matrix B,int y){
    Matrix C;
    C.n = C.m = n;
    for (reg int i = 1;i <= n; ++ i)
        C.c[i][i] = 1;
    while (y){
        if (y & 1)
            C = C * B;
        B = B * B;
        y >>= 1;
    }
    return C;
}
 
int js(int x,int y){
    return (x - 1) * Maxn + y;
}
 
int main(){
    //freopen("1.out","w",stdout);
    scanf("%d%d",&n,&T);
    for (reg int i = 1;i <= n; ++ i){
        for (reg int j = 1;j <= n; ++ j){
            scanf("%1d",&A[i][j]);
            if (Maxn < A[i][j])
                Maxn = A[i][j];
        }
    }
    for (reg int i = 1;i <= n; ++ i){
        for (reg int j = 1;j < Maxn; ++ j)
            B.c[js(i,j)][js(i,j + 1)] = 1;
        for (reg int j = 1;j <= n; ++ j)
            if (A[i][j])
                B.c[js(i,A[i][j])][js(j,1)] = 1;
    }
    n *= Maxn;
    B.n = B.m = n;
    G = Qkpow(B,T);
    printf("%d\n",G.c[1][n - Maxn + 1]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章