[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
分析
(剛開始還以爲老師誤把圖論題放到矩陣加速這一板塊來了呢)
讀入雖然爲字符串,但我們將它轉換一下,不就是一個矩陣了嗎?
就以 樣例一 爲例,可得
顯然這是鄰接矩陣,如果此矩陣中的 = 1 的話就表示 i 到 j 是連通的
然而,對於這種01矩陣我們發現有一個特殊的性質:我們用這個01矩陣乘自己的話,不正表示從 i 到 j 花費 T 時間的方案數嗎?
首先從是有 條路可走的,而從有 條路可走,那麼根據乘法原理,從就有個方案數到 k。
所以我們只需要一個qkpow即可求出答案
但是很尷尬的是,這僅限於 01矩陣,對於這個邊權可爲9的矩陣,真是一點辦法兒也沒有呢 ……
所以這時候我們需要運用“拆點”這一新奇的想法
拆點
拆點,對於此題而言,其實就是將一個點分最大權值個點,這是什麼意思呢?我們就假設有一個邊權 <= 2的2階方陣,如圖(以下圖片來源:GM的神奇PPT)
畫出行程圖:
於是我們可以就可以將1、2號節點拆成兩個(畢竟此矩陣中最大權值爲2)
但是我們還是以 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 函數開始看
其實,中合起來就可以看做一個節點,怎麼理解呢?
結合第一個 for 循環:就是將自己連起來(也就是1.1 連 1.2這種)
b[(i,j)][(i,j + 1)]結合一下上面的圖一下就理解了。
接下來就是函數了, 這個節點的前一列的最後一個是第(i - 1)* Maxn點,那麼再加上一個 j,不就又相當於又會新開一列,且剛好會在第 j 個位置。
舉個栗子:
要找到,我們假設有一個人在0號節點位置,它肯定得走 Maxn 個點才能到達1.Maxn,然後需要再走一個點才能達到2.1,最後他就僅僅只需要走兩個點便能到達2.3了,不是嗎?
((2 - 1) Maxn + 1 + (3 - 1) = (2 - 1) 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;
}