公式

E - 魔法の道 / Magic Road 解説 by kyopro_friends


この問題は行列累乗により解くことができます。

\(i\) から街 \(j\) への道が存在しないとき、代わりに「魔力値 \(0\) の道路が存在する」としても答えは変化しません(存在しない道を通る経路の魔力は \(0\) になり、答えに影響しないため)。よってそのように仮定します。

\(A_{i,j}\) を街 \(i\) から街 \(j\) への道の魔力値とします。

\(f_k(i,j)\) を「ちょうど \(K\) 本の道を通って街 \(i\) から街 \(j\) へ行く経路の魔力の総和」と定めます。求める答えは \(f_K(1,N)\) です。

ところで、「最後に街 \(j\) に移動する直前にどの街にいたか」を考えることで、 \(f_K(i,j)=\sum_{m=1}^{N}f_{K-1}(i,m)A_{m,j}\) となることがわかります。

例えば \(K=3\) のときを考えると、

\(\begin{aligned} f_3(1,N) &=\sum_{x=1}^{N}\sum_{y=1}^{N}A_{1,x}A_{x,y}A_{y,N}\\ &=\sum_{y=1}^{N}\left(\sum_{x=1}^{N}A_{1,x}A_{x,y}\right)A_{y,N}\\ &=\sum_{y=1}^{N}f_2(1,x)A_{y,N} \end{aligned}\)

よって、行列 \(F_k\) 及び \(A\)

\(F_k=\begin{pmatrix} f_k(1,1) & \dots & f_k(1,N) \\ \dots &\dots &\dots \\ f_k(N,1) & \dots & f_k(N,N) \\ \end{pmatrix}\)

\(A=\begin{pmatrix} A_{1,1} & \dots & A_{1,N} \\ \dots &\dots &\dots \\ A_{N,1} & \dots & A_{N,N} \\ \end{pmatrix}\)

と定めると、\(F_k = F_{k-1}A\) となり、明らかに \(F_1=A\) であることから、\(F_k=A^k\) となります。よって求める答え \(f_K(1,N)\)\(A^K\)\((1,N)\) 成分です。

行列積の計算は定義通り行うことで \(O(N^3)\) でできるので、繰り返し二乗法により \(O(N^3\log K)\)\(A^K\) を求めることができます。

実装例 (C++)

#include<bits/stdc++.h>
#include<atcoder/modint>
using namespace std;
using mint = atcoder::modint998244353;
using mat = vector<vector<mint>>;

mat matmul(mat x, mat y){
  int n = x.size();
  int m = y.size();
  int l = y[0].size();
  mat ret(n, vector<mint>(l));
  for(int i=0; i<n; i++){
    for(int j=0; j<m; j++){
      for(int k=0; k<l; k++){
        ret[i][k] += x[i][j] * y[j][k];
      }
    }
  }
  return ret;
}

mat matpow(mat a, int k){
  int n = a.size();
  mat ret(n, vector<mint>(n));
  for(int i=0; i<n; i++){
    ret[i][i] = 1;
  }
  while(k){
    if(k&1){
      ret = matmul(ret, a);
    }
    a = matmul(a, a);
    k /= 2;
  }
  return ret;
}

int main(){
  int n, m, k;
  cin >> n >> m >> k;
  mat a(n, vector<mint>(n));

  for(int i=0; i<m; i++){
    int u, v, w;
    cin >> u >> v >> w;
    u--, v--;
    a[u][v]=w;
  }

  auto b = matpow(a, k);
  cout << b[0][n-1].val() << endl;
}

実装例 (Python)

MOD = 998244353

def matmul(x, y):
  n = len(x)
  m = len(y)
  l = len(y[0])
  ret = [[0] * l for _ in range(n)]
  for i in range(n):
    for j in range(m):
      for k in range(l):
        ret[i][k] += x[i][j] * y[j][k]
        ret[i][k] %= MOD
  return ret

def matpow(a, k):
  n = len(a)
  ret = [[0] * n for _ in range(n)]
  for i in range(n):
    ret[i][i] = 1
  while k:
    if k & 1:
      ret = matmul(ret, a)
    a = matmul(a, a)
    k //= 2
  return ret

def main():
  N, M, K = map(int, input().split())
  a = [[0] * N for _ in range(N)]
  for i in range(M):
    U, V, W = map(int, input().split())
    U -= 1
    V -= 1
    a[U][V] = W
  b = matpow(a, K)
  print(b[0][-1])

main()

投稿日時:
最終更新: