Codeforces Round #852 (Div. 2)
题意
区间长度为n的数组a[i],且满足a[i]∈[1,n],有q个查询区间[l1,ri],对于每个查询区间,求出
ali,ali+1,⋯,ari−1,ari 中相差最小的值,即
j,k∈[li,ri]min∣aj−ak∣
思路
该题不好直接构造线段树维护差值最小值,考虑离线做法.
令当前考虑右端点在 r 处的查询 Sr={[li,ri]:ri=r},设 dp(i) 为 ai 与 {ai+1,⋯,ar} 的最小差值,即 dp(i)=min{∣ai−aj∣:i+1⩽j⩽r}.
从左到右依次扫描 r,考虑如何使用 ar 更新 1⩽i⩽r−1 的dp值.
设 l1=max{l:al⩾ar} 为距离 r 最近且比 ar 大的值,若 i<l1<r 且 ai⩾2al1+ar,则 ai 离 al1 的距离不低于 ai,所以无需更新. 所以要找
l2=max{l:ar⩽al⩽⌊2al1+ar⌋}
依此类推:
lk+1=max{l:ar⩽al⩽⌊2alk+ar⌋}
直到 alk=ar 或者 ∅={l:ar⩽al⩽⌊2alk+ar⌋},于是我们只需更新 l∈{l1,⋯,lk} 的dp值为 dp(l)=min{dp(l),al−ar}.
对于 al<ar 与 al>ar 处理类似,只需找
lk+1=max{l:⌊2alk+ar⌋⩽al⩽ar}
由于值域为 [1,n] 则更新节点数为 O(logn),用线段树查找 lk 用时 O(logn),总计用时 O(nlog2n).
对于每个询问 [li,ri],当右端点 r=ri 时,答案为 li⩽l<rimindp(l),用另一颗线段树记录dp值即可,用时 O(qlogn).
具体算法步骤:
- 离线全部询问,以右端点进行排序.
- 构造两颗线段树:
- key: Id, value: dp值,找区间最小值
- key: ai,value: Id,找区间最大值.
点击显/隐代码