【题解】p4159:SCOI 2009 迷路:(矩乘优化递推)

\(Description:\)

该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。 现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?

\(Sample Input 1\):

2 2
11
00

\(Sample Output:\)

1

很明显(不明显

关于邻接矩阵有一个神奇的性质,如果邻接矩阵中只有0/1代表两点是否连通,那么从s走到t且恰好经过k条边的方案总数,就是该邻接矩阵的k次幂中[s,t]的值。

对于只有0,1的矩阵我们考虑一开始的矩阵\(f_1 [i][j]\)就表示\(i\)\(j\)一步可以走到的方案数。

\(f_t [i][j]\)表示走了t步的路径条数

那么只要对每一次的矩阵\(f_t[i][j]\)可以从\(f_{t-1}[i][j]\)\(f_1[i][j]\)乘一乘得到。

转移方程:
\(f_t[i][j]=\sum_{k=1}^{n}f_t[i][j]*f_1[i][j]\)

又发现(没发现这个可以用矩乘加速。

那么我们把这个代码码出来,然后发现好像过不了边权不为1,0的情况,好惊恐~~

然后我们考虑把这些边权附加到点上。

把每个点拆成9个

那么没输入一个边权只要强制这个点走到这个点的第边权个就可以转换成01矩阵了。

性质为什么我好不太理解。。。

#include<bits/stdc++.h>
using namespace std;
int n,t,m;
const int MAXN=100;
const int p=2009;
char s[MAXN+5][MAXN+5];
struct matrix{
    int a[MAXN+5][MAXN+5];
    inline void clear(){
        memset(a,0,sizeof(a));
    }
    inline matrix operator *= (matrix b){
        matrix ret;ret.clear();
        for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int k=1;k<=n;++k)
            ret.a[i][j]=(ret.a[i][j]+this->a[i][k]*b.a[k][j]%p)%p;
        return *this=ret;
    }
};
inline matrix power(matrix a,int b){
    matrix ret;ret.clear();
    for(int i=1;i<=n;++i)ret.a[i][i]=1;
    for(;b;a*=a,b>>=1)if(b&1)ret*=a;
    return ret;
}
matrix f;
int main(){
    f.clear();
    scanf("%d%d",&n,&t);
    for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
    m=n;n=9*n;
    for(int i=1;i<=m;++i)for(int j=1;j<=8;++j)
        f.a[(i-1)*9+j][(i-1)*9+j+1]=1;
    for(int i=1;i<=m;++i)for(int j=1;j<=m;++j){
        int x=s[i][j]^48;
        if(!x)continue;
        f.a[(i-1)*9+x][(j-1)*9+1]=1;
    }
    f=power(f,t);
    printf("%d\n",f.a[1][m*9-8]);
    return 0;
}

转载于:https://www.cnblogs.com/JCNL666/p/10623158.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值