筑波大学プログラミングコンテスト2024
コンテスト日時
2024/11/17 (Su) 12:00 - 16:00

L - Matrix on SegmentTree

Flavor
2
s
1024
MB
800

問題文

高橋君は次の問題を解いています。

$N$ 個の行列の列 $X$ が与えられる。左から $i$ 番目の行列は $M_{i-1}$ 行 $M_i$ 列である。
$Q$ 個のクエリが順に与えられる。$i$ 番目のクエリは次のような形式である。

  • タイプ $1$: 1 x B $x$ 番目の行列を $M_{x-1}$ 行 $M_x$ 列の行列 $B$ に更新する。
  • タイプ $2$: 2 l r $l$ 番目から $r$ 番目までの行列の行列積を計算し出力する。

高橋君はこの問題に、下記の条件を満たす二分木を用いて答えます。

  • $2N-1$ 個の頂点からなり、各頂点には番号 $1,2,\cdots,2N-1$ がついている。
  • 各頂点 $i$ は区間 $[L_i, R_i]$ を持つ。
  • 頂点 $1$ は根であり、区間 $[1, N]$ を持つ。
  • 頂点 $i$ が葉であるときかつそのときに限り $L_i = R_i$ である。
  • 葉でない頂点には $c_i, d_i$ の $2$ 個の子が存在する。また、葉でない頂点が持つ区間を $[i, j]$ とすると、この頂点の子が持つ区間は $[i, k]$ と $[k + 1, j]$ である。($i \leq k < j$)
  • 各頂点 $i$ は、$M_{L_i-1}$ 行 $M_{R_i}$ 列の行列 $A_i$ を持つ。最初、 $A_i$ は $X_{L_i}$ から $X_{R_i}$ までの行列の行列積に等しい。

更に、高橋君は次のような疑似コードを書きました。次の方法によってクエリを処理します。

  • タイプ $1$ のクエリが与えられたとき、f(1, x, B) を呼び出し各頂点の持つ行列 $A_i$ を更新する。
  • タイプ $2$ のクエリが与えられたとき、g(1, l, r) を呼び出し答えを計算する。
fun f($n$, $x$, $B$):
    if $[L_n, R_n] = [x, x]$:
        $A_n$ = $B$
        return
    if $x \in [L_{c_n}, R_{c_n}]$:
        f($c_n$, $x$, $B$)
    if $x \in [L_{d_n}, R_{d_n}]$:
        f($d_n$, $x$, $B$)
    $A_n$ = mult($A_{c_n}$, $A_{d_n}$)

fun g($n$, $l$, $r$):
    if $[L_n, R_n] \subset [l, r]$:
        return $A_n$
    if $[l, r] \cap [L_{c_n}, R_{c_n}] = \emptyset$:
        return g($d_n$, $l$, $r$)
    if $[l, r] \cap [L_{d_n}, R_{d_n}] = \emptyset$:
        return g($c_n$, $l$, $r$)
    return mult(g($c_n$, $l$, $r$), g($d_n$, $l$, $r$))

fun mult($A$, $B$):
    $C$ = $O_{A.\mathrm{row},B.\mathrm{col}}$
    for $i$ in $[0, A.\mathrm{row})$:
        for $j$ in $[0, B.\mathrm{col})$:
            for $k$ in $[0, A.\mathrm{col})$:
                $C_{i,j}$ += $A_{i,k} * B_{k,j}$
    return $C$

ただし、$O_{n,m}$ は $n$ 行 $m$ 列の零行列、$A.\mathrm{row}, A.\mathrm{col}$ はそれぞれ行列 $A$ の行数、列数を表します。

行列積の計算は重いことに気づいた高橋くんは、二分木をうまく決めることで行列積にかかる計算量を最小化することにしました。
具体的には、$x$ 行 $y$ 列の行列と $y$ 行 $z$ 列の行列の行列積を計算するのにかかるコストを $xyz$ として、すべてのクエリを処理するときにかかるコストの総和を最小化したいです。
あなたは与えられるクエリの内容を事前に知っています。高橋君の代わりに二分木をうまく決め、コストの総和の最小値を求めてください。

制約

  • $1 \leq N \leq 500$
  • $1 \leq M_i \leq 10^4 (0 \leq i \leq N)$
  • $1 \leq Q \leq 1000$
  • タイプ $1$ のクエリにおいて、$1 \leq x \leq N$
  • タイプ $2$ のクエリにおいて、$1 \leq l \leq r \leq N$
  • 入力はすべて整数である。

部分点

この問題には、部分点が設定されている。部分点の採点方法については、コンテストトップ を参照すること。

  1. ($200$ 点)
  • $1 \leq N \leq 50$
  1. ($200$ 点)
  • $1 \leq N \leq 200$
  1. ($400$ 点)
  • 追加の制約はない。

入力

入力は以下の形式で標準入力から与えられる。

$N$

$M_0$ $M_1$ $\cdots$ $M_N$

$Q$

$\mathrm{query_1}$

$\mathrm{query_2}$

$\vdots$

$\mathrm{query_Q}$

$\mathrm{query_i}$ は $i$ 番目のクエリを表しており、以下のいずれかの形式で与えられる。

$1$ $x$

$2$ $l$ $r$

行列の列 $X$ や更新される行列 $B$ は入力で与えられないことに注意せよ。

出力

コストの総和としてあり得る最小値を出力せよ。

入力例 1
4 2 3 3 4 5 3 1 2 2 1 3 2 1 2
出力例 1
72
  • $(c_1, d_1, L_1, R_1) = (2, 3, 1, 4)$
  • $(c_2, d_2, L_2, R_2) = (4, 5, 1, 2)$
  • $(c_3, d_3, L_3, R_3) = (6, 7, 3, 4)$
  • $(L_4, R_4) = (1, 1)$
  • $(L_5, R_5) = (2, 2)$
  • $(L_6, R_6) = (3, 3)$
  • $(L_7, R_7) = (4, 4)$

とするのが最適です。これは完全二分木をなします。このとき、最初のクエリでは f(1, 2, B), f(2, 2, B), f(5, 2, B) が呼び出され、

  • 区間 $[1, 1]$ と区間 $[2, 2]$ の表す行列の積
  • 区間 $[1, 2]$ と区間 $[3, 4]$ の表す行列の積

が計算されます。コストはそれぞれ $2 \times 3 \times 3 = 18, 2 \times 3 \times 5 = 30$ です。
$2$ 番目のクエリでは、g(1, 1, 3), g(2, 1, 3), g(3, 1, 3), g(6, 1, 3) が呼び出され、

  • 区間 $[1, 2]$ と区間 $[3, 3]$ の表す行列の積

が計算されます。コストは $2 \times 3 \times 4 = 24$ です。
$3$ 番目のクエリでは、g(1, 3, 4), g(3, 3, 4) が呼び出され、行列積の計算はありません。
コストの合計は $30 + 18 + 24 = 72$ であり、コストをこれより小さくすることはできません。
各頂点が持つ行列の初期値の計算にコストがかからないことなどに注意してください。

この入力は部分点 $1,2$ の制約を満たします。

入力例 2
9 3 1 4 1 5 9 2 6 5 3 9 1 9 1 9 1 8 1 2 1 4 1 4 1 3 1 5 1 3
出力例 2
408

タイプ $2$ のクエリがなくても行列を更新する必要があります。

この入力は部分点 $1,2$ の制約を満たします。

入力例 3
10 3310 566 5758 7263 4555 5294 2853 9861 7265 5039 7734 10 1 7 2 3 4 2 4 5 2 3 3 2 6 9 2 8 10 2 4 6 1 6 2 4 7 1 7
出力例 3
1148983422423

出力は $32$ bit 整数型に収まらない可能性があることに注意してください。

この入力は部分点 $1,2$ の制約を満たします。

提出
C++23 (g++ 12.2.0)