题目大意
windywindywindy在有向图中迷路了。
该有向图有 NNN 个节点,windywindywindy从节点 000 出发,他必须恰好在 TTT 时刻到达节点 N−1N-1N−1。
现在给出该有向图,你能告诉windywindywindy总共有多少种不同的路径吗?
注意:windywindywindy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
题目解析
由于这道题的 TTT 很大,线性算法是过不了的,所以我们考虑矩阵乘法。
这道题图中的边权为 1 91~91 9,比较小,所以考虑拆点,为了方便表示,把编号为 iii的点拆为 i∗10+1 i∗10+9i*10+1~i*10+9i∗10+1 i∗10+9 一共 999 个节点。如果 iii 到 jjj 有一条权值为 kkk 的路径,那么就把矩阵中[i∗10+k,j∗10+1][i*10+k,j*10+1][i∗10+k,j∗10+1]的值设为 1,相当于先从 i∗10+1→i∗10+ki*10+1→i*10+ki∗10+1→i∗10+k 走过 k−1k-1k−1条长度为 111 的路径,再在 i∗10+k→j∗10+1i*10+k→j*10+1i∗10+k→j∗10+1 走过 111 条长度为 111 的路径,总长度为kkk,跑一遍快速幂,[1∗10+1][n∗10+1][1*10+1][n*10+1][1∗10+1][n∗10+1]的值即是方案总数。
代码
#include<bits/stdc++.h>
#define N 105
#define M 2009
using namespace std;
int n,t,an;
int m[N][N],tmp[N][N],ans[N][N];
char c;
void mul(int a[N][N],int b[N][N])
{
memset(tmp,0,sizeof(tmp));
for(int i=0;i<an;i++)
for(int j=0;j<an;j++)
for(int k=0;k<an;k++)
tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%M;
memcpy(a,tmp,sizeof(tmp));
}
void qpow()
{
for(int i=0;i<an;i++) ans[i][i]=1;
while(t)
{
if(t&1) mul(ans,m);
mul(m,m);
t>>=1;
}
}
int main()
{
cin>>n>>t;
an=n*9;
for(int i=0;i<n;i++)
{
for(int j=0;j<8;j++)
m[i*9+j][i*9+j+1]=1;
for(int j=0,num;j<n;j++)
{
cin>>c;
num=c-'0';
if(num) m[i*9+num-1][j*9]=1;
}
}
qpow();
cout<<ans[0][(n-1)*9];
}