D - 三角形ボードの最適経路 / Optimal Path on a Triangular Board 解説
by
MMNMM
まず、非負整数 \(x\) に対して \(f(x)\) の値を求めることを考えます。
これは \(x\) の桁数を \(d\) とすると、\(S\left(\left\lfloor\dfrac{x+1}{10 ^ {d-1}}\right\rfloor\times10 ^ {d-1}-1\right)\) となることが示せます(\(\left\lfloor\dfrac{x+1}{10 ^ {d-1}}\right\rfloor\times10 ^ {d-1}-1\) は、最上位以外の桁がすべて \(9\) であるような整数のうち、\(x\) を超えない最大のものです)。
これを用いて計算した \(f(A _ {i,j})\) を改めて \(A _ {i,j}\) とおくと、(\(L\lt P\) となる問い合わせを取り除いた上で)解くべき問題は次のようになります。
\(L\le i\lt L+T,P\le j\le P+i-L\) を満たす \((i,j)\ (1\le i\le N)\) にわたる \(A _ {i,j}\) の最大値を求めよ。
これは、Sparse Table のような構造を用いることでクエリあたり \(3\) つの値の最大値を求めることで解くことができます。
具体的には、\(t _ 1=1,t _ {i+1}=t _ {i}+\lceil t _ i/2\rceil\) について、すべてのマス \((l,p)\) に対する問い合わせ \((l,p,t _ i)\) の結果を \((l,p,t _ {i-1})\) から求めることができます。

前計算の時間計算量および空間計算量は \(O(N ^ 2\log N)\) 、クエリあたりの計算量は \(O(\log N)\) などとできます。
この問題では \(NQ\) に対する制約があるため、クエリあたり \(O(N)\) 時間などをかける解法であっても十分高速です。
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N, Q;
cin >> N >> Q;
vector<vector<int>> A(N);
for (int i = 0; i < N; ++i) {
A[i].resize(i + 1);
for (int& a : A[i]) {
// f(A[i][j]) を求める
cin >> a;
string S = to_string(a + 1);
a = S[0] + size(S) * 9 - 58; // 越えない範囲で 9 を詰め込んだものが最大
}
}
// 三角形領域に対する Sparse Table
vector<vector<vector<int>>> doubling{A};
for (int d = 1; d <= N; d += (d + 1) / 2) {
int w = (d + 1) / 2;
vector<vector<int>> next = doubling.back();
for (int i = 0; i < N; ++i) {
for (int j = 0; j <= i; ++j) {
next[i][j] = max({doubling.back()[i][j], doubling.back()[min(i + w, N - 1)][j], doubling.back()[min(i + w, N - 1)][j + min(i + w, N - 1) - i]});
}
}
doubling.emplace_back(next);
}
for (int i = 0; i < Q; ++i) {
int L, P, T;
cin >> L >> P >> T;
--L;
--P; // 0-indexed にしておく
if (P > L) { // 外側なら NA
cout << "NA" << endl;
continue;
}
T = min(T + 1, N - L);
int w = 1, level = 0;
while (w + (w + 1) / 2 < T) {
w += (w + 1) / 2;
++level;
}
// 前計算の結果を適切に取ることで O(1) 個の最大値が答えになるようにできる
cout << max({doubling[level][L][P], doubling[level][L + T - w][P], doubling[level][L + T - w][P + T - w]}) << endl;
}
return 0;
}
N, Q = map(int, input().split())
# f(S) を求める
def f(S):
S = str(int(S) + 1)
return int(S[0]) - 10 + len(S) * 9 # 越えない範囲で 9 を詰め込んだものが最大
A = [list(map(f, input().split())) for i in range(N)]
# 三角形領域に対する Sparse Table
doubling = [A]
d = 1
while d <= N:
w = (d + 1) // 2
next = [[0 for i in r] for r in A]
for i in range(N):
for j in range(i + 1):
next[i][j] = max(doubling[-1][i][j], doubling[-1][min(i + w, N - 1)][j], doubling[-1][min(i + w, N - 1)][j + min(i + w, N - 1) - i])
doubling.append(next)
d += w
for i in range(Q):
L, P, T = map(int, input().split())
L -= 1
P -= 1 # 0-indexed にしておく
if P > L: # 外側なら NA
print('NA')
continue
T = min(T + 1, N - L)
w = 1
level = 0
while w + (w + 1) // 2 < T:
w += (w + 1) // 2
level += 1
# 前計算の結果を適切に取ることで O(1) 個の最大値が答えになるようにできる
print(max(doubling[level][L][P], doubling[level][L + T - w][P], doubling[level][L + T - w][P + T - w]))
投稿日時:
最終更新:
