3 番目のシミュレーションは、最初の 2 つのシミュレーション (
水のバッチ) の一貫したスタイルを継続しました。難しくはありませんが、まだ大丈夫な質問がいくつかあります。前に学んだ知識を復習するために最後の質問を説明しましょう。
問題の説明:
Xiaolan にはシーケンス a[1]、a[2]、...、a[n] があります。正の整数 k が与えられた場合、1 から n までの各シリアル番号 ia[ik] について、2k+1 個の数値のうちの a[i-k+1]...a[i+K] の最小値はいくらですか
? 添字が 1 から n の範囲を超える場合、その数値は存在せず、最小値を計算するときに存在する値のみが使用されます。
入力形式:
入力の最初の行には整数 n が含まれています。2 行目には、それぞれ a[1]、a[2]、...、a[n] を表す n 個の整数が含まれています。3 行目には整数 k が含まれています
出力フォーマット:
各フォント サイズで取得された最小値をそれぞれ表す n 個の整数を含む行を出力します。
入力例:
5
5 2 7 4 3
1
出力例:
2 2 2 3 3
評価ユースケースの規模と合意
評価ケースの 30% では、1 <= n <= 1000、1 <= a[i] <= 1000。
評価ケースの 50% では、1 <= n <= 10000、1 <= a[i] <= 10000。
すべての評価ケースで、1 <= n <= 1000000、1 <= a[i] <= 1000000
アイデア 1:ツリー配列
まず、ツリー配列について説明します。
まず質問の意味を掘り出し、シーケンスを走査し、走査された要素の範囲の最大値を見つけます。走査が進むにつれて、区間内の要素が変化します。つまり、要素を変更する必要があります。区間内の最大の値については、バイナリ メソッドを使用して見つけることができます。
具体的な方法は、現在トラバースされている要素の特定のサイズ範囲 (2k + 1) でレコード値のツリー状配列を維持することです。各走査では二分探索を使用して、この間隔の最小値を見つけます。
コード
N = 100010
tr = [0] * N
a = [0] * N
def lowbit(x) : return x & -x
def add(x, c) :
i = x
while i <= n :
tr[i] += c
i += lowbit(i)
def ask(x) :
res = 0
i = x
while i :
res += tr[i]
i -= lowbit(i)
return res
def init() : # 初始化,将第一个元素右边的m个元素初始化树状数组
for i in range(1, m + 1) :
add(a[i], 1)
n = int(input())
a[1 : n + 1] = list(map(int, input().split()))
maxn = max(a[1 : n + 1])
m = int(input())
init()
for i in range(1, n + 1) :
if i + m <= n : # 随着区间的挪动添加元素
add(a[i + m], 1)
if i - m - 1 >= 1 : # # 随着区间的挪动删除元素
add(a[i - m - 1], -1)
l, r = 0, maxn # 二分查找,右半段查找最小值
while l < r :
mid = (l + r) >> 1
if ask(mid) >= 1 :
r = mid
else :
l = mid + 1
print(l, end = " ")
アイデア 2:スライディング ウィンドウ
上記の説明から、それが引き違い窓であることは明らかです。
ウィンドウ サイズ 2*k+1 で単調減少するキューを維持するだけです。
コード
N = 1000010
a = [0] * N
q = [0] * N
n = int(input())
a[1 : n + 1] = list(map(int, input().split()))
m = int(input())
hh, tt = 0, -1
def init() : # 初始化,将前m个进队列,这时只需要保持单调性即可
global tt
for i in range(1, m + 1) :
while hh <= tt and a[q[tt]] >= a[i] :
tt -= 1
tt += 1
q[tt] = i
init()
for i in range(m + 1, n + m + 1) :
while hh <= tt and i - q[hh] + 1 > 2 * m + 1 :
hh -= 1
if i <= n : # 防止序列越界
while hh <= tt and a[q[tt]] >= a[i] :
tt -= 1
tt += 1
q[tt] = i
print(a[q[hh]], end = " ")
アイデア 3:線分ツリー
考え方はツリー配列や範囲クエリと同じです。
N = 1000010
class Tree :
def __init__(self) :
self.l = 0
self.r = 0
self.v = 0
tr = [Tree() for _ in range(N * 4)]
a = [0] * N
def pushup(u) :
tr[u].v = min(tr[u << 1].v, tr[u << 1 | 1].v)
def build(u, l, r) :
tr[u].l, tr[u].r = l, r
if l == r :
tr[u].v = a[l]
return
mid = l + r >> 1
build(u << 1, l, mid)
build(u << 1 | 1, mid + 1, r)
pushup(u)
def query(u, l, r) :
if l <= tr[u].l and tr[u].r <= r :
return tr[u].v
res = 10000010
mid = tr[u].l + tr[u].r >> 1
if l <= mid :
res = query(u << 1, l, r)
if r > mid :
res = min(res, query(u << 1 | 1, l, r))
return res
n = int(input())
a[1 : n + 1] = list(map(int, input().split()))
m = int(input())
build(1, 1, n)
for i in range(1, n + 1) :
l, r = max(1, i - m), min(i + m, n)
print(query(1, l, r), end = " ")
アイデア 4: RMQ
from math import log
N = 1000010
a = [0] * N
f = [[1000010] * 25 for _ in range(N)]
n = int(input())
a[1 : n + 1] = list(map(int, input().split()))
m = int(input())
def init() : # 预处理st表
for i in range(1, n + 1) :
f[i][0] = a[i]
k = int(log(n, 2)) + 1
for length in range(1, k) :
for l in range(1, n + 1) :
r = l + (1 << length) - 1
if r > n : break
f[l][length] = min(f[l][length - 1], f[l + (1 << (length - 1))][length - 1])
def query(l, r) :
k = int(log(r - l + 1, 2))
return min(f[l][k], f[r - (1 << k) + 1][k])
init()
for i in range(1, n + 1) :
l, r = max(1, i - m), min(i + m, n)
print(query(l, r), end = " ")