牛客小白赛13,E,矩阵乘法的实际意义,矩阵快速幂

本文介绍了一种使用矩阵快速幂解决图论中可达矩阵的高效算法,通过实例讲解了矩阵乘法和快速幂的基本原理及应用。文章详细解释了如何通过矩阵运算求解特定天数下节点间可达性的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:E-小A的路径

自己写的时候想跑SPFA,然后把每个点的入度乘以出度再求和。后来发现不对,对于任意一个点来说,有可能在多个不同的天数进入,这样就不好确定它到底是在哪天入队的,算法会变得非常复杂。

看完题解才想起来离散数学里面讲的可达矩阵,当时还手算五阶可达阵的六次方(课上手算,简直算得想死......)

那么另一个问题来了:矩阵快速幂。

以前也没写过矩阵快速幂,借着机会学一下了。

typedef long long ll;

struct Matrix{
    ll a[N][N];
    Matrix() { memset(a,0,sizeof(a)); }
    Matrix operator* (const Matrix &rhs) const {
        Matrix res;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    res.a[i][j]=(res.a[i][j]+a[i][k]*rhs.a[k][j])%mod;
        return res;
    }
}G,res;
 

void fpow(int k) {
    for(int i=1;i<=n;i++) res.a[i][i]=1; //n是阶数,相当于普通快速幂的 ans=1;
    while(k) {
        if(k&1) res=res*G;
        G=G*G;
        k>>=1; 
    }
}

另外,板子记不住就得自己写矩阵乘法,下面是伪代码:

matrix_multipul(res,G){
//原矩阵	G[n][n];
//结果矩阵 	res[n][n];
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= n ; j ++){
			for(int k = 1 ; k <= n ; k ++){
				res[i][j]=(res[i][j]+G[i][k]+G[k][j]);
			}
		}
	} 
	return res;
}

矩阵左乘和右乘的结果是不一样的,但是res和G的都是由G自乘得来的,可以验证此处乘法作为矩阵快速幂的子函数用,乘法顺序不影响结果。但是如果在其他的题目里面就要注意左乘还是右乘。

牛客网标程:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519540

//标程 
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define Rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll INF=9e18;
const int N=1e2+50;
const ll mod=1e9+7;
int n,m,k,s;
struct Matrix{
    ll a[N][N];
    Matrix() { memset(a,0,sizeof(a)); }
    Matrix operator* (const Matrix &rhs) const {
        Matrix res;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    res.a[i][j]=(res.a[i][j]+a[i][k]*rhs.a[k][j])%mod;
        return res;
    }
}G,res;
void fpow(int k) {
    while(k) {
        if(k&1) res=res*G;
        G=G*G;
        k>>=1; 
    }
}
int main() {    
    scanf("%d%d%d%d",&n,&m,&k,&s);
    for(int i=1;i<=m;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        G.a[u][v]++;
    }
    for(int i=1;i<=n;i++) res.a[i][i]=1; 
    fpow(k);
    ll sum=0;
    for(int i=1;i<=n;i++)
        if(i!=s) sum+=res.a[s][i],sum%=mod;
    printf("%lld\n",sum);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值