D - Permutation Subsequence
キーワード
解説
良い添字列は の連続する長さ の部分列に対して、それぞれの においてのインデックスを取り出したもの。
全ての良い添字列に対して、その中で最大の値と最小の値の差の最小値を求める問題。つまり、全ての の連続する長さ の部分列に対して、P においてのインデックスで最大の値と最小の値の差の最小値を求めればよい。
一つ目の方法として、セグ木を使う方法がある。
二つのセグ木を用意して、それぞれのセグ木において、最大値と最小値を持っておく。これによって、全ての の連続する長さ の部分列に対して、最大値と最小値を求めることができる。
それぞれのセグ木には、 番目に を入れておく。それらに対して、 の区間でクエリを行う。
この場合、計算量は となる。
二つ目の方法としては、平衡二分探索木を用いる方法がある。
平衡二分探索木は、挿入、削除、クエリが で行えるデータ構造である。普通の Set
型より挿入、削除、クエリが遅くなるが、 最大値と最小値の取得が で行える。
とおいて、 の区間に対して、最大値と最小値を取得すればよい。
を ずつスライドさせ、そのたびに を 削除し、 を挿入することで、長さ の良い添字列の集合を作ることができる。
これによって、全ての の連続する長さ の部分列に対して、最大値と最小値を求めることができる。
こちらの場合も、計算量は となる。
Go
で実装する場合は、github.com/liyue201/gostl/ds/set
(opens in a new tab)を使えばよい。
提出コード
- セグ木を使った解法
セグ木の実装はこちらから。
func main() {
var n, k int
scanIntVariables(&n, &k)
P := readInts()
maxT := NewSegmentTree(n, 0, func(a, b int) int {
return max(a, b)
})
minT := NewSegmentTree(n, math.MaxInt32, func(a, b int) int {
return min(a, b)
})
for i := 0; i < n; i++ {
maxT.Update(P[i]-1, i)
minT.Update(P[i]-1, i)
}
ans := math.MaxInt32
for i := 0; i < n-k+1; i++ {
maxP := maxT.Query(i, i+k)
minP := minT.Query(i, i+k)
ans = min(ans, maxP-minP)
}
writeLine(ans)
}
- 平衡二分探索木を使った解法
func main() {
var n, k int
scanIntVariables(&n, &k)
P := readInts()
Q := make([]int, n)
for i := 0; i < n; i++ {
Q[P[i]-1] = i
}
st := set.New(func(a, b int) int { return a - b })
for i := 0; i < k; i++ {
st.Insert(Q[i])
}
ans := st.Last().Value() - st.Begin().Value()
for i := k; i < n; i++ {
st.Erase(Q[i-k])
st.Insert(Q[i])
ans = min(ans, st.Last().Value()-st.Begin().Value())
}
writeLine(ans)
}