嘘~ 正在从服务器偷取页面 . . .

Atcoder Educational DP Contest R Walk 题解


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;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录