H - 展望台の配置 / Placement of Observation Decks Editorial by admin
gpt-5.5-high概要
展望スポットを好きな順に並べ、隣り合う箇所からちょうど \(K\) 箇所を選んで、標高差の絶対値の合計を最大化する問題です。
結論として、標高をソートし、「低い標高」と「高い標高」を交互に並べる形を考えることで最大値を求められます。
考察
まず、選んだ吊り橋だけに注目します。
標高 \(x < y\) の 2 点を結ぶ吊り橋のスリル値は
\(y - x\)
です。つまり、この吊り橋は
- 高い方に \(+1\)
- 低い方に \(-1\)
の係数を与える、と考えられます。
また、1 つの展望スポットは、並びの中で左右高々 2 箇所の吊り橋にしか接しません。
したがって、ある標高が \(+1\) や \(-1\) として数えられる回数は高々 2 回です。
最大化するには、
- \(+\) の係数はできるだけ大きい標高に付けたい
- \(-\) の係数はできるだけ小さい標高に付けたい
です。
そのため、使うべき標高はソート後の「小さい側」と「大きい側」に集中します。
また、吊り橋を複数の離れたパスに分けると、端点が増えます。
端点は係数の絶対値が \(1\) にしかなりませんが、内部の点は係数の絶対値を \(2\) にできます。
したがって、最大値は \(K\) 本の吊り橋を 1 本のパスとして使い、標高を
低い・高い・低い・高い・…
のように交互に並べることで達成できます。
残りの使わない展望スポットは、この列の外側に置けばよいです。
ソート後の配列を 0-indexed で
\(A_0 \leq A_1 \leq \cdots \leq A_{N-1}\)
とします。
また、
- \(S(t)\): 小さい方から \(t\) 個の和
- \(L(t)\): 大きい方から \(t\) 個の和
とします。
\(K\) が奇数の場合
\(K = 2m - 1\) とします。
このとき、使う頂点数は \(K+1 = 2m\) 個です。
交互に並べると、
- 高い側を \(m\) 個使う
- 低い側を \(m\) 個使う
ことになります。
内部の点は係数が \(\pm 2\)、端点は係数が \(\pm 1\) です。
高い側では、最も小さいものを端点にして \(+1\)、残りを \(+2\) にします。
低い側では、最も大きいものを端点にして \(-1\)、残りを \(-2\) にします。
よって答えは
\(2(L(m) - S(m)) - (A_{N-m} - A_{m-1})\)
です。
例えば、\(K=3\)、標高が \(1,2,99,100\) の場合、
\(2((99+100)-(1+2)) - (99-2) = 295\)
となり、実際に
\(2,100,1,99\)
と並べると
\(|2-100| + |100-1| + |1-99| = 98+99+98=295\)
です。
\(K\) が偶数の場合
\(K = 2m\) とします。
このとき、使う頂点数は \(K+1 = 2m+1\) 個です。
交互に並べると、端点は両方とも
- 高い側になる
- 低い側になる
のどちらかです。
端点が高い側の場合
高い側を \(m+1\) 個、低い側を \(m\) 個使います。
- 低い側 \(m\) 個はすべて内部なので係数 \(-2\)
- 高い側 \(m+1\) 個のうち、端点 2 個は係数 \(+1\)
- 残り \(m-1\) 個は係数 \(+2\)
よって候補値は
\(-2S(m) + A_{N-m-1} + A_{N-m} + 2L(m-1)\)
です。
端点が低い側の場合
低い側を \(m+1\) 個、高い側を \(m\) 個使います。
- 高い側 \(m\) 個はすべて内部なので係数 \(+2\)
- 低い側 \(m+1\) 個のうち、端点 2 個は係数 \(-1\)
- 残り \(m-1\) 個は係数 \(-2\)
よって候補値は
\(2L(m) - \left(2S(m-1) + A_{m-1} + A_m\right)\)
です。
この 2 つのうち大きい方が答えです。
アルゴリズム
- 標高配列 \(A\) を昇順にソートする。
- 累積和
prefを作る。 - 以下を定義する。
sum_small(t)= 小さい方から \(t\) 個の和sum_large(t)= 大きい方から \(t\) 個の和
- \(K\) の偶奇で場合分けする。
- \(K\) が奇数なら、上の奇数用の式で答えを計算する。
- \(K\) が偶数なら、端点が高い場合・低い場合の 2 通りを計算し、大きい方を答えにする。
計算量
- 時間計算量: \(O(N \log N)\)
- ソートが支配的です。
- 空間計算量: \(O(N)\)
- 配列と累積和を持ちます。
実装のポイント
答えは最大でおよそ \(10^6 \times 10^9 = 10^{15}\) になるため、
long longを使います。偶数の場合、\(m=1\) のとき
sum_small(m-1)やL(m-1)は 0 個の和になります。累積和を使うと自然に処理できます。添字は 0-indexed で考えると、コード中の式と対応しやすいです。
ソースコード
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, K;
cin >> N >> K;
vector<long long> A(N);
for (auto &x : A) cin >> x;
sort(A.begin(), A.end());
vector<long long> pref(N + 1, 0);
for (int i = 0; i < N; i++) pref[i + 1] = pref[i] + A[i];
auto sum_small = [&](int t) -> long long {
return pref[t];
};
auto sum_large = [&](int t) -> long long {
return pref[N] - pref[N - t];
};
long long ans;
if (K % 2 == 1) {
int m = (K + 1) / 2;
ans = 2 * (sum_large(m) - sum_small(m)) - (A[N - m] - A[m - 1]);
} else {
int m = K / 2;
long long ans1 = -2 * sum_small(m)
+ A[N - m - 1] + A[N - m]
+ 2 * (pref[N] - pref[N - m + 1]);
long long ans2 = 2 * sum_large(m)
- (2 * sum_small(m - 1) + A[m - 1] + A[m]);
ans = max(ans1, ans2);
}
cout << ans << '\n';
return 0;
}
この解説は gpt-5.5-high によって生成されました。
posted:
last update: