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
5 30
12045
07105
47805
12024
12345
852
(刚开始还以为老师误把图论题放到矩阵加速这一板块来了呢)
读入虽然为字符串,但我们将它转换一下,不就是一个矩阵了吗?
就以 样例一 为例,可得
( 1 1 0 0 ) \begin{pmatrix} 1 & 1 \\ 0 & 0\\ \end{pmatrix} (1010)
显然这是邻接矩阵,如果此矩阵中的 ( i , j ) \begin{pmatrix} i,j\end{pmatrix} (i,j) = 1 的话就表示 i 到 j 是连通的
然而,对于这种01矩阵我们发现有一个特殊的性质:我们用这个01矩阵乘自己的话,不正表示从 i 到 j 花费 T 时间的方案数吗?
首先从 ( i , j ) \begin{pmatrix} i,j\end{pmatrix} (i,j)是有 x x x 条路可走的,而从 ( j , k ) \begin{pmatrix} j,k\end{pmatrix} (j,k)有 y y y 条路可走,那么根据乘法原理,从 ( i , k ) \begin{pmatrix} i,k\end{pmatrix} (i,k)就有 x × y x \times y x×y个方案数到 k。
所以我们只需要一个qkpow即可求出答案
但是很尴尬的是,这仅限于 01矩阵,对于这个边权可为9的矩阵,真是一点办法儿也没有呢 ……
所以这时候我们需要运用“拆点”这一新奇的想法
拆点,对于此题而言,其实就是将一个点分最大权值个点,这是什么意思呢?我们就假设有一个边权 <= 2的2阶方阵,如图(以下图片来源:GM的神奇PPT)
画出行程图:
于是我们可以就可以将1、2号节点拆成两个(毕竟此矩阵中最大权值为2)
( 1.1 2.1 1.2 2.2 ) \begin{pmatrix} 1.1 & 2.1\\ 1.2 & 2.2\\ \end{pmatrix} (1.11.22.12.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 函数开始看
( 1.1 2.1 3.1 ⋯ n . 1 1.2 2.2 3.2 ⋯ n . 2 1.3 2.3 3.3 ⋯ n . 3 ⋮ ⋮ ⋮ ⋮ ⋮ 1. M a x n 2. M a x n 3. M a x n ⋯ n . M a x n ) \begin{pmatrix} 1.1 & 2.1 & 3.1 & \cdots & n.1 \\ 1.2 & 2.2 & 3.2 & \cdots & n.2 \\ 1.3 & 2.3 & 3.3 & \cdots & n.3 \\ \vdots & \vdots & \vdots & \vdots & \vdots \\ 1.Maxn & 2.Maxn & 3.Maxn & \cdots & n.Maxn \\ \end{pmatrix} ⎝⎜⎜⎜⎜⎜⎛1.11.21.3⋮1.Maxn2.12.22.3⋮2.Maxn3.13.23.3⋮3.Maxn⋯⋯⋯⋮⋯n.1n.2n.3⋮n.Maxn⎠⎟⎟⎟⎟⎟⎞
其实, ( i − 1 ) ∗ M a x n + j (i - 1) * Maxn + j (i−1)∗Maxn+j中 i i i j j j合起来就可以看做一个节点,怎么理解呢?
结合第一个 for 循环:就是将自己连起来(也就是1.1 连 1.2这种)
b[ g m gm gm(i,j)][ g m gm gm(i,j + 1)]结合一下上面的图一下就理解了。
接下来就是 g m gm gm函数了, i . j i.j i.j这个节点的前一列的最后一个是第(i - 1)* Maxn点,那么再加上一个 j,不就又相当于又会新开一列,且刚好会在第 j 个位置。
举个栗子:
要找到 2.3 2.3 2.3,我们假设有一个人在0号节点位置,它肯定得走 Maxn 个点才能到达1.Maxn,然后需要再走一个点才能达到2.1,最后他就仅仅只需要走两个点便能到达2.3了,不是吗?
((2 - 1) × \times × Maxn + 1 + (3 - 1) = (2 - 1) × \times × Maxn + 3)
#include
#include
#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;
}