题解 【P4159 [SCOI2009] 迷路】
题目和普通的 \(01\) 路径矩阵加速有一点区别,做法很巧。
\(\large{\texttt{Meaning}}\)
给定一个邻接矩阵,即每个点之间的边权,若为 \(0\) 则无边,因为是 \(a*a\) 的矩阵,所以隐藏含义是每条边权 \(1\)~\(9\) 。
\(\large{\texttt{Solution}}\)
因为边权不只是 \(1\) 了,所以不能直接就将每个点连接边做矩乘,但是数据范围太大又不能不用矩乘。
注意到边权为 \(1\)~\(9\) ,所以可以将每一个点拆成 \(9\) 个点,若原先每个点为 \(i\) ,则拆后的点为 \((i-1)*9+n~(1\le n\le9)\), \(i\) 的主点为 \((i-1)*9+1\) ,并将点 \((i-1)*9+n\) 和 \((i-1)*9+n+1\) 连边长为 \(1\) 。
然后连长度不为 \(1\) 的边就很方便了,若连点 \(u\) 和 \(v\) 边权为 \(k\) ,则连接 \((u-1)*9+k\) 和 \((v-1)*9+1\) ,这样,从 \(u\) 的主点必须要走 \(k-1\) 个单位,再走这条边才到的主点,且可以使用矩阵乘法+快速幂。
最后答案即为 \(1\) 的主点到 \(n\) 的主点的距离。
\(\large{\texttt{Code}}\)
#include<bits/stdc++.h>
using namespace std;
// #define int long long
const int mod=2009;
const int N=10;
int a,b;
char ch;
struct node {
int n,m,s[1010][1010];
inline void Mem() {memset(s,0,sizeof(s));}
inline void Re() {for(int i=1;i<=n;i++) s[i][i]=1;}
inline void print() {for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) printf("%d ",s[i][j]); puts("");}}
node operator*(const node b) {
node sum;
sum.Mem();
sum.n=n;
sum.m=b.m;
for(int i=1;i<=n;i++) for(int j=1;j<=b.m;j++) {for(int k=1;k<=m;k++) sum.s[i][j]=(sum.s[i][j]+s[i][k]*b.s[k][j]); sum.s[i][j]%=mod;}
return sum;
}
} S,A;
inline void Pow(int n) {//矩阵快速幂
while(n) {
if(n&1) S=S*A;
A=A*A;
n>>=1;
}
}
signed main() {
// freopen("in","r",stdin);
scanf("%d%d",&a,&b);
S.n=S.m=a*9;
for(int i=1;i<=a;i++) for(int j=1;j<=8;j++) S.s[(i-1)*9+j][(i-1)*9+j+1]=1;
for(int i=1;i<=a;i++) {
getchar();//防止scanf读入偏差
for(int j=1;j<=a;j++) {
scanf("%c",&ch);
if(ch!='0') S.s[(i-1)*9+ch-'0'][(j-1)*9+1]=1;
}
}
A=S;
Pow(b-1);
//S.print();
printf("%d",S.s[1][(a-1)*9+1]);
return 0;
}