AGC005-B:Minimum Sum
問題
https://beta.atcoder.jp/contests/agc005/tasks/agc005_b
N個の数 {1, 2, 3, ..., n} の任意の順列 a_n が与えられる。
を求めよ。
考え方
ナイーブにシミュレーションするとO(N2)となり間に合わない。
N2 つの区間の最小値を求めて和を求めるのではなく、最小値が a_i になるときの区間がいくつ存在するか?というように見方を変えると高速に求められる。
最小値が a_i になる区間の個数を求めるために
(1)区間の左側に a_i よりも小さい数に到達するまでの距離
(2)区間の右側に a_i よりも大きな数に到達するまでの距離
を計算する。
(1) * (2) の個数が区間 [0, n-1] の中でa_i が最小値になる個数である。
サンプル3を例に考える。例えば a_i = 0 とすると、区間 [0, n-1] での左右への距離は以下のようになる。なお入力の a_i は 0-indexed としておく。
|
4 | 3 | 7 | 0 | 1 | 5 | 6 | 2 |
---|---|---|---|---|---|---|---|---|
距離 | 3 | 2 | 1 | 0 | 1 | 2 | 3 | 4 |
左側には 3 つ、右側へは 4 つ距離を取ることができるので、合計で 1 は (3+1) * (4 + 1) = 20 回使用される。 a_i それぞれについてこの距離を求めればよい。番兵として、-1 と n = 8 をおいておくと考えやすくなる。
|
-1 | 4 | 3 | 7 | 0 | 1 | 5 | 6 | 2 | 8 |
---|---|---|---|---|---|---|---|---|---|---|
距離 | 4 | 3 | 2 | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
実装上も少し工夫が必要で、入力される a_i に対して区間上の index を保持しておくと考えやすい。
以下のような形になる。
|
4 | 3 | 7 | 0 | 1 | 5 | 6 | 2 |
---|---|---|---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
あとは、最小の a_i = 0 から貪欲に左右の距離を求めていく。これはTreeSetなどの平衡二分木で O(log N) で求められるので、全体の計算量は O(N log N) である。
public void solve(int testNumber, InputReader in, PrintWriter out) { int n = in.nextInt(); int[] m = new int[n]; for (int idx = 0; idx < n; idx++) { int i = in.nextInt()-1; m[i] = idx; } TreeSet<Integer> set = new TreeSet<>(); set.add(-1); set.add(n); long ans = 0; for (int i = 0; i < n; i++) { int idx = m[i]; long l = idx - set.lower(idx); long r = set.higher(idx) - idx; ans += l * r * (i+1); set.add(idx); } out.println(ans); }