ABC 429
Table of Contents
https://atcoder.jp/contests/abc429
A. Too Many Requests
https://atcoder.jp/contests/abc429/tasks/abc429_a
B. N - 1
https://atcoder.jp/contests/abc429/tasks/abc429_b
C. Odd One Subsequence
https://atcoder.jp/contests/abc429/tasks/abc429_c
D. On AtCoder Conference
https://atcoder.jp/contests/abc429/tasks/abc429_d
E. Hit and Away
https://atcoder.jp/contests/abc429/tasks/abc429_e
F. Shortest Path Query
https://atcoder.jp/contests/abc429/tasks/abc429_f
2026/1/30 segment tree で解けると知った状態で自力 AC
行番号、列番号は 0-indexedとする。
まず各列の状態を bit で表現する。$i$ 行目が # なら $i$ ビット目を立てる。
状態 $x \in [0, 8)$ である列に対して $i$ 行目から $j$ 行目への遷移にかかる最小コストを $C^x_{i,j}$ と定義する。
ここで、$i$ 行目が # で塞がっている場合、任意の $j$ について $C^x_{i,j} = \infty$ とするし、$j$ 行目が # で塞がっている場合、任意の $i$ について $C^x_{i,j} = \infty$ とする。
すべてを8通り全ての状態について列挙すると以下のようになる
\begin{align*} C^0 &= \begin{pmatrix}0 & 1 & 2 \\ 1 & 0 & 1 \\ 2 & 1 & 0\end{pmatrix}, & C^1 &= \begin{pmatrix}\infty & \infty & \infty \\ \infty & 0 & 1 \\ \infty & 1 & 0\end{pmatrix}, & \\ C^2 &= \begin{pmatrix}0 & \infty & \infty \\ \infty & \infty & \infty \\ \infty & \infty & 0\end{pmatrix}, & C^3 &= \begin{pmatrix}\infty & \infty & \infty \\ \infty & \infty & \infty \\ \infty & \infty & 0\end{pmatrix}, & \\ C^4 &= \begin{pmatrix}0 & 1 & \infty \\ 1 & 0 & \infty \\ \infty & \infty & \infty\end{pmatrix}, & C^5 &= \begin{pmatrix}\infty & \infty & \infty \\ \infty & 0 & \infty \\ \infty & \infty & \infty\end{pmatrix}, & \\ C^6 &= \begin{pmatrix}0 & \infty & \infty \\ \infty & \infty & \infty \\ \infty & \infty & \infty\end{pmatrix}, & C^7 &= \begin{pmatrix}\infty & \infty & \infty \\ \infty & \infty & \infty \\ \infty & \infty & \infty\end{pmatrix}. \end{align*}
ここで状態 $x$ の $i$ 行目から状態 $y$ の $j$ 行目への遷移コストを $C_{i, j}^{x, y}$ と定義すると
\begin{align*} C_{i,j}^{xy} = \min_{k} \left(C^x_{i,k} + C^y_{k,j} \right)+1 \end{align*}
となる。これは状態 $x$ の $i$ 行目から状態 $y$ の $j$ 行目へ遷移する際、状態 $x$ の $i$ 行目から状態 $x$ の $k$ 行目へ遷移し、列を跨いで状態 $y$ の $k$ 行目へ遷移し、状態 $y$ の $k$ 行目から状態 $y$ の $j$ 行目へ遷移することを意味している。
$C^{xy} = C^x \star C^y$ となる演算子 $\star$ を定義すると、$\star$ は結合法則を満たす。
また
\begin{align*} E = \begin{pmatrix}0 & \infty & \infty \\ \infty & 0 & \infty \\ \infty & \infty & 0\end{pmatrix} \end{align*}
となる単位元 $E$ も存在する。
よって $(\star, E)$ がモノイドなので segment tree に乗せることができる。
各クエリで状態を更新した後、全区間の積を求め、$C_{0,2}$ を答えればよい。
struct S {
vvll mat;
S() {
mat = vvll(3, vll(3, INF));
}
S operator*(const S& other) {
S v;
rep(i, 3) rep(j, 3) {
ll t = INF;
rep(k, 3) {
// 列を跨ぐ回数分 +1 するのは外でまとめてやる
chmin(t, mat[i][k] + other.mat[k][j]);
}
v.mat[i][j] = t;
}
return v;
}
};
S op(S a, S b) {
return a * b;
}
S e() {
S ret;
ret.mat = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}};
return ret;
}
void solve() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
ll N;
cin >> N;
vint state(N);
{
vector<string> grid(3);
rep(i, 3) cin >> grid[i];
rep(j, N) {
int t = 0;
rep(i, 3) {
if (grid[i][j] == '#') t += 1 << i;
}
state[j] = t;
}
}
// 状態の行列を事前計算
vector<S> V(8);
rep(st, 1 << 3) {
S tmp;
rep(i, 3) {
vll v(3, INF);
if (st >> i & 1) continue;
// index, cost
using P = pair<ll, ll>;
queue<P> que;
v[i] = 0;
que.push({i, 0});
while (que.size()) {
auto [id, cost] = que.front();
que.pop();
for (auto d : {-1, 1}) {
ll nid = id + d;
if (clamp(nid, 0ll, 2ll) != nid) continue;
if (!(st >> nid & 1) && v[nid] > cost + 1) {
v[nid] = cost + 1;
que.push({nid, cost + 1});
}
}
}
rep(j, 3) tmp.mat[i][j] = v[j];
}
V[st] = tmp;
}
segtree<S, op, e> seg(N);
rep(i, N) {
seg.set(i, V[state[i]]);
}
ll Q;
cin >> Q;
while (Q--) {
ll x, y;
cin >> x >> y;
x--, y--;
state[y] ^= 1 << x;
seg.set(y, V[state[y]]);
S ret = seg.prod(0, N);
ll ans = INF;
chmin(ans, N - 1 + ret.mat[0][2]); // 列を跨ぐ回数は N - 1 回
if (ans == INF) ans = -1;
cout << ans << '\n';
}
}