1. 程式人生 > >[BZOJ1297][SCOI2009]迷路(拆點+矩陣乘法)

[BZOJ1297][SCOI2009]迷路(拆點+矩陣乘法)

題目描述

傳送門

題解

由於矩陣的冪只能處理邊權為1的情況,又由於邊權最大隻到9,可以將一個點拆成9個點,分別表示路徑的分段長度。
比如說1->2,5
這裡寫圖片描述
求矩陣的冪即可。

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int Mod=2009;
const int max_n=15;
const int max_N=105;
struct hp{int a[max_N][max_N];}unit,A,ans;
char
s[max_n]; int squ[max_n][max_n]; int n,t; inline hp cheng(hp a,hp b) { hp ans; memset(ans.a,0,sizeof(ans.a)); for (int i=1;i<=n*9;++i) for (int j=1;j<=n*9;++j) for (int k=1;k<=n*9;++k) ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j])%Mod; return
ans; } inline hp matrix_fast_pow(hp a,int p) { hp ans=unit; for (;p;p>>=1,a=cheng(a,a)) if (p&1) ans=cheng(ans,a); return ans; } int main() { scanf("%d%d\n",&n,&t); for (int i=1;i<=n*9;++i) unit.a[i][i]=1; for (int i=1;i<=n;++i) { gets(s); for
(int j=1;j<=n;++j) squ[i][j]=s[j-1]-'0'; } for (int i=1;i<=n;++i) for (int j=2;j<=8;++j) A.a[n+(i-1)*8+j-1][n+(i-1)*8+j]=1; for (int i=1;i<=n;++i) A.a[i][n+(i-1)*8+1]=1; for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (squ[i][j]) { if (squ[i][j]==1) A.a[i][j]=1; else A.a[n+(i-1)*8+squ[i][j]-1][j]=1; } ans=matrix_fast_pow(A,t); printf("%d\n",ans.a[1][n]); }

總結

這個拆點的思想很厲害呀。要注意以後不同的思想要融會貫通,比如說拆點在網路流裡很常用,這裡用的也非常巧妙。