[C++][SCOI2009]迷路

文章目录

  • [SCOI2009]迷路
    • 题目描述
      • 输入格式:
      • 输出格式:
        • 输入样例:
        • 输出样例
      • #1:
        • 输入样例:
        • 输出样例:
    • 分析
    • 拆点
      • 小结代码
    • 代码

[SCOI2009]迷路

题目描述

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:

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

画出行程图:
[C++][SCOI2009]迷路_第1张图片
于是我们可以就可以将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连起来(相当于一个点,自己都走不到自己要它也就没什么用了)

[C++][SCOI2009]迷路_第2张图片
接着,就得开始把 1 和 2两个节点连起来,此时我们要凑 2,只需要把1.2 和 2.1连起来,1.1 到 1.2就为 2了呀
[C++][SCOI2009]迷路_第3张图片
注意,有自环的也要连

小结代码

[C++][SCOI2009]迷路_第4张图片
不知你有没有看不懂的绝望,反正我刚开始,是一脸懵呢
首先,我们从这个 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.31.Maxn2.12.22.32.Maxn3.13.23.33.Maxnn.1n.2n.3n.Maxn
其实, ( i − 1 ) ∗ M a x n + j (i - 1) * Maxn + j (i1)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;
}

你可能感兴趣的:(矩阵加速,数论)