[BZOJ 1297][SCOI2009]迷路
1297: [SCOI2009]迷路
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1418 Solved: 1017
[Submit][Status][Discuss]Description
windy在有向圖中迷路了。 該有向圖有 N 個節點,windy從節點 0 出發,他必須恰好在 T 時刻到達節點 N-1。 現在給出該有向圖,你能告訴windy總共有多少種不同的路徑嗎? 註意:windy不能在某個節點逗留,且通過某有向邊的時間嚴格為給定的時間。
Input
第一行包含兩個整數,N T。 接下來有 N 行,每行一個長度為 N 的字符串。 第i行第j列為‘0‘表示從節點i到節點j沒有邊。 為‘1‘到‘9‘表示從節點i到節點j需要耗費的時間。
Output
包含一個整數,可能的路徑數,這個數可能很大,只需輸出這個數除以2009的余數。
Sample Input
【輸入樣例一】
2 2
11
00
【輸入樣例二】
5 30
12045
07105
47805
12024
12345
Sample Output
【輸出樣例一】
1
【樣例解釋一】
0->0->1
【輸出樣例二】
852
HINT
30%的數據,滿足 2 <= N <= 5 ; 1 <= T <= 30 。 100%的數據,滿足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
一周之前做的了...想了想還是寫寫題解吧...
題解
首先我們都知道的一個結論是對於一個簡單圖 $G$ 的鄰接矩陣 $M$ , $M^k_{i,j}$ 就是從點 $i$ 走 $k$ 條邊到點 $j$ 的方案數.
然而這個結論只適用於兩點間的邊數, 而我們註意到本題中兩點間的邊是帶權的, 我們就不能直接使用這個結論了
接著我們可以想到的其中一個解法是把一條邊權為 $k$ 的邊通過在其中插入虛擬結點的方式拆成 $k$ 條邊, 但是極端情況下可能有 $100$ 條帶權邊要拆, 每條帶權邊可能會拆成 $10$ 條無權邊, 其中會產生大量虛擬結點, 極端情況下總結點數會大於 $1000$ , 矩陣相應的也會變成這個數量級, 然後一次 $O(n^3)$ 的矩陣乘法都跑不完...╮(╯﹏╰)╭
繼續思考優化方式, 現在的瓶頸在於虛擬結點過多, 我們可以思考如何縮減虛擬結點的數量. 我們可以嘗試事先將一些虛擬結點和原結點連成鏈, 然後對於帶權邊都指向這條鏈上的對應位置, 將帶權邊前方的鏈合並起來(一股Trie的味道?)來最小化結點數量, 這樣就可以把結點數量控制在 $100$ 的量級
然後就很棒棒了, 構造完矩陣無腦跑快速冪就好了(o?▽?)o
反正別像我一樣打出了正解輸出的時候把倍增矩陣當成答案矩陣輸出就行了( ̄. ̄)
參考代碼
GitHub
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 7 const int MAXN=110; 8 const int MOD=2009; 9 10 int n,t; 11 char buf[MAXN]; 12 int m[MAXN][MAXN]; 13 int x[MAXN][MAXN]; 14 int mt[MAXN][MAXN]; 15 16 int Encode(int); 17 void Initialize(); 18 int Encode(int,int); 19 20 int main(){ 21 Initialize(); 22 // t--; 23 while(t>0){ 24 if((t&1)!=0){ 25 memset(mt,0,sizeof(mt)); 26 for(int i=0;i<n*9;i++) 27 for(int j=0;j<n*9;j++) 28 for(int k=0;k<n*9;k++) 29 (mt[i][j]+=x[i][k]*m[k][j])%=MOD; 30 memcpy(x,mt,sizeof(mt)); 31 } 32 memset(mt,0,sizeof(mt)); 33 for(int i=0;i<n*9;i++) 34 for(int j=0;j<n*9;j++) 35 for(int k=0;k<n*9;k++) 36 (mt[i][j]+=m[i][k]*m[k][j])%=MOD; 37 memcpy(m,mt,sizeof(mt)); 38 t>>=1; 39 } 40 printf("%d\n",x[0][Encode(n-1)]); 41 return 0; 42 } 43 44 void Initialize(){ 45 scanf("%d%d",&n,&t); 46 for(int i=0;i<n;i++){ 47 for(int j=2;j<=9;j++){ 48 m[Encode(i,j)][Encode(i,j-1)]=1; 49 } 50 } 51 for(int i=0;i<n;i++){ 52 scanf("%s",buf); 53 for(int j=0;j<n;j++){ 54 if(buf[j]!=‘0‘){ 55 m[Encode(i)][Encode(j,buf[j]-‘0‘)]=1; 56 } 57 } 58 } 59 for(int i=0;i<n*9;i++) 60 x[i][i]=1; 61 } 62 63 inline int Encode(int k,int len){ 64 return k*9+len-1; 65 } 66 67 inline int Encode(int k){ 68 return k*9; 69 }Backup
[BZOJ 1297][SCOI2009]迷路