Atcoder Educational DP Contest R Walk 题解
题目链接:Atcoder Educational DP Contest R Walk
题意:
给一张 $N$ 个结点的有向简单图,给出邻接矩阵 $A$ ,求长度为 $K$ 的路径条数,答案对 $10^9+7$ 取模。
$1 \le N \le 50,1\le K \le 10^{18}$ , $a_{i,j}$ is 0 or 1 ,$a_{i,i}=0$
设 $f_{k,i,j}$ 表示 $i$ 到 $j$ 路径长为 $k$ 的方案数,则
之所以不从 $k-t$ 转移,而从 $k-1$ 转移,是因为可能存在重复计数
比如这张图
就可能重复计数
上面的式子可以进一步化简
这个形式是不是很熟悉?
考虑矩阵快速幂优化dp
关于为什么可以用矩阵快速幂,这里解释一下
由于矩阵乘法的定义为
而这里的形式和矩阵乘法的定义完全一致
因此我们可以看作三个矩阵的关系,即
稍微化一化柿子,可得
至此可知,答案就是
时间复杂度 $O(n^3 \log k)$
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef vector< vector<int> > mat;
#define N (int)()
const int mod=1e9+7;
void add(int &x,int y){x+=y; if(x>=mod) x-=mod;}
mat mul(mat A,mat B)
{
int n=A.size(),m=A[0].size(),p=B[0].size();
mat C(n,vector<int>(p));
for(int k=0; k<m; k++)
for(int i=0; i<n; i++)
for(int j=0; j<p; j++)
add(C[i][j],A[i][k]*B[k][j]%mod);
return C;
}
mat qpow(mat A,int k)
{
int n=A.size();
mat B(n,vector<int>(n));
for(int i=0; i<A.size(); i++) B[i][i]=1;
while(k)
{
if(k&1) B=mul(B,A);
A=mul(A,A); k>>=1;
}
return B;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int n,k; cin >> n >> k;
mat a(n,vector<int>(n));
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
cin >> a[i][j];
int res=0; a=qpow(a,k);
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
add(res,a[i][j]);
cout << res << '\n';
return 0;
}