[SCOI2009]迷路(矩阵快速幂) 题解
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
2 2
11
00
【输入样例二】
5 30
12045
07105
47805
12024
12345
Sample Output
【输出样例一】
1
【样例解释一】
0->0->1
【输出样例二】
852
1
【样例解释一】
0->0->1
【输出样例二】
852
HINT
30%的数据,满足 2 <= N <= 5 ; 1 <= T <= 30 。 100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
结论:对于边权都相同的邻接矩阵$G$,$G^T$表示两点间长度为$T$的路径的方案数。
考虑矩阵乘法在这类题中的实际意义:
$a[i][j]=\sum b[i][k]*b[k][j]$ 可以把k看作枚举的中继,就得到了更进一步的方案数
即
$(i->j)的方案数 = (i->k)的方案数 * (k->j)的方案数 \ (起点终点固定)$
自乘$n-1$次即可得到两点间长度为n的方案数。
虽然这道题带权,但可以注意到边权种类很少,完全可以在一条路上强行加点起到统一边权的效果。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=105,mod=2009; struct matrix { int a[N][N]; matrix() { memset(a,0,sizeof(a)); } }g; int n,T; matrix operator * (matrix x,matrix y) { matrix res; for(int i=0;i<n*9;i++) for(int j=0;j<n*9;j++) for(int k=0;k<n*9;k++) (res.a[i][j]+=x.a[i][k]*y.a[k][j])%=mod; return res; } matrix qpow(matrix a,int b) { matrix res=a; while(b) { if(b&1)res=res*a; a=a*a;; b>>=1; } return res; } int main() { scanf("%d%d",&n,&T); char s[N]; for(int i=0;i<n;i++) { scanf("%s",s); for(int j=0;j<n;j++) { if(s[j]=='0')continue; int x=s[j]-'0'; x--; g.a[i*9+x][j*9]=1; } for(int j=0;j<8;j++) g.a[i*9+j][i*9+j+1]=1; } matrix res=qpow(g,T-1); cout<<res.a[0][9*(n-1)]<<endl; return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。