题目链接: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;
}