[矩阵加速]道路千万条

题目描述

道路千万条,安全第一条!HF校区到YB校区有很多种走法,我们可以把走法看成N个节点的有向图,假设HF代表0号节点,YB代表N-1号节点,GM想从0号节点出发,到N-1号节点,但必须恰好在T时刻到达!你能告诉GM一共有多少种走法吗?注意:GM不能在某个节点逗留,且通过某有向边时严格为给定时间(边权)。

输入

满足 2 <= N <= 10 ; 1 <= T <= 1000000000。
输出
包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。
样例输入
2 2
11
00
样例输出
1

解题思路

首先想一个简单的情况,如果只是有0,1所组成的矩阵我们该如何求呢
例如\begin{bmatrix} 1 & 0\\ 0 & 1 \end{bmatrix}

这里有一个性质,也就是这一个矩阵的t次幂所在的s[i][j]正是从i到j的时间为t的路径数。

震惊吧,我也很震惊...

仔细想一想其实也确实是这样。

我们先看2次方

\begin{bmatrix} 1 * 1 + 0 * 0&1 * 0 + 0 * 1\\ 0 * 1 + 1 * 0&1 * 1 + 0 * 1 \end{bmatrix}

(1,1)就是意义为1到1中途经过1的情况,就相当于走了2(时间)。而(1,2)则是1到2中途经过2的情况,这样推下去确实也没有问题,因为(i,j)为1则就说明了i,j有路。

其实我们回到矩阵乘法的基本定义

其实也就这么做出来。

但这道题又不是由0,1构成,我们就需要拆点。

例如\begin{bmatrix} 0 & 2\\ 2 & 1 \end{bmatrix}

我们就拆成了

\begin{bmatrix} 0 & 1 & 0 &0 \\ 0 &0 &1 &0 \\ 0 & 0 & 1 &1 \\ 1 & 0 & 0 &0 \end{bmatrix}

我们就是将每一个点拆成它的最大边权个(这样才能构造出全部为1,0)

拆点过程呢...看代码吧,我觉得自己想一下还是可以理解的。(模板大法好)

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
struct matrix {
    int n,m;
    long long c[150][150];
    matrix (){
        memset(c,0,sizeof(c));
    }
    matrix operator * (const matrix &x)const{
        matrix tmp ;
        tmp.n = n;
        tmp.m = x.m;
        for (int i = 1;i <= tmp.n;i ++)
            for (int j = 1;j <= tmp.m;j ++)
                for (int k = 1;k <= x.n;k ++)
                    tmp.c[i][j] = (tmp.c[i][j] + c[i][k] * x.c[k][j]) % 2009;
        return tmp;
    }
}A,B;
int n,t;
long long maxn;
int xb(int x,int y){
    return (x - 1) * maxn + y;
}
matrix qkpow(matrix x,int y){
    matrix ans;
    ans.n = ans.m = x.n;
    for (int i = 1;i <= ans.n;i ++)
        ans.c[i][i] = 1;
    while (y){
        if (y & 1)
            ans = ans * x;
        x = x * x;
        y /= 2;
    }
    return ans;
}
int main(){
    scanf ("%d%d",&n,&t);
    for (int i = 1;i <= n;i ++)
        for (int j = 1;j <= n;j ++){
            scanf ("%1lld",&A.c[i][j]);
            maxn = max(A.c[i][j],maxn);
        }
    for (int i = 1;i <= n;i ++){
        for (int j = 1;j < maxn;j ++)
            B.c[xb(i,j)][xb(i,j+1)] = 1;
        for (int j = 1;j <= n;j ++){
            if (A.c[i][j] != 0)
                B.c[xb(i,A.c[i][j])][xb(j,1)] = 1;
        }
    }
    B.n = B.m = maxn * n;
    B = qkpow(B,t);
    printf("%lld\n",B.c[1][(n - 1) * maxn + 1]);
}

花絮

广搜爆炸

深搜爆炸

(搜索真好用)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值