Struct nekolib::ds::incremental_line_set::IncrementalLineSet
source · pub struct IncrementalLineSet<I: Ord> { /* private fields */ }
Expand description
直線の集合。
以下のクエリを処理する。
- 集合 で初期化する。
- 集合 に 1 次関数 を追加する。
- 集合 中の関数における、 での最小値を返す。
言い換えると、直線の追加クエリと、特定の 座標での 座標の最小値を求めるクエリを捌く。いわゆる CHT。
Idea
次の二つの連想配列を管理する。
- を与えると、 なる を返す。
- を与えると、 が最小となる の最大値 を返す。
- こちらは双方向で管理しておく。すなわち、 の連想配列も持つ。
保持しておく必要がある直線を対応する区間の昇順に並べると、傾きの降順に並ぶことに気づく。 そこで、追加したい直線の傾きより小さい最大の傾きの直線と、大きい最小の直線と比較し、 新しい直線が必要かどうかをまず確かめる。 それが必要なら、追加する直線に近い方から順にすでにある直線を見ていき、 必要なものが見つかるまで削除する。
クエリを整数とすると、以下が成り立つ。
Complexity
演算 | 時間計算量 |
---|---|
new | |
push | |
min |
ここで、 は から必要のない直線を除いたものからなる集合である。
Applications
次の形式の DP の高速化に使える。 の部分が、直線 の における最小値に相当するためである。 の値を求めた後、直線 を追加していけばよい。ここで、 や は を含んでもよいし含まなくてもよい。どちらにも が含まれない場合には、特に DP 配列のようなものを用意する必要はない。
たとえば、次のようなものが当てはまる。
お気に入りの例として、次のような問題 も解ける:
整数列 が与えられる。 これの空でもよい区間 に対し、次の値を考える。 全ての区間の選び方におけるこの値の最大値を求めよ。
Sample
[5, -1000, 1, -3, 7, -8] [ ------ ] => 1 * 1 + (-3) * 2 + 7 * 3 = 16
、 とおくと、次のように変形できる。
右端 を固定したときの最大値を とおくと、 とできる。よって、上記の枠組みで 、、 、 としたものと見なせ、 や の計算を適切に高速化すれば、 時間で解ける。
Examples
use nekolib::ds::IncrementalLineSet;
let mut ls = IncrementalLineSet::new();
assert_eq!(ls.min(0), None);
ls.push((2, 2));
assert_eq!(ls.min(0), Some(2));
assert_eq!(ls.min(2), Some(6));
ls.push((1, 3));
assert_eq!(ls.min(0), Some(2));
assert_eq!(ls.min(2), Some(5));
assert_eq!(ls.min(5), Some(8));
ls.push((-1, 10));
assert_eq!(ls.min(2), Some(5));
assert_eq!(ls.min(5), Some(5));
assert_eq!(
format!("{:?}", ls),
r"{\x. 2x+2: ..=1, \x. x+3: ..=3, \x. -x+10: ..=2147483647}"
);
use nekolib::ds::IncrementalLineSet;
let a = vec![5, -1000, 1, -3, 7, -8];
let n = a.len();
let sigma = {
let mut sigma = vec![0; n + 1];
for i in 0..n {
sigma[i + 1] = sigma[i] + a[i];
}
sigma
};
let tau = {
let mut tau = vec![0; n + 1];
for i in 0..n {
tau[i + 1] = tau[i] + a[i] * (i + 1) as i64;
}
tau
};
let p = |j: usize| tau[j] - j as i64 * sigma[j];
let q = |j: usize| j as i64;
let r = |i: usize| sigma[i];
let s = |i: usize| tau[i];
let mut ls = IncrementalLineSet::new();
let mut dp = vec![0; n + 1];
ls.push((q(0), p(0)));
for i in 1..=n {
dp[i] = -ls.min(r(i)).unwrap() + s(i);
ls.push((q(i), p(i)));
}
let res = *dp.iter().max().unwrap();
assert_eq!(res, 1 * 1 + (-3) * 2 + 7 * 3);
References
Implementations§
Trait Implementations§
source§impl<I: Clone + Ord> Clone for IncrementalLineSet<I>
impl<I: Clone + Ord> Clone for IncrementalLineSet<I>
source§fn clone(&self) -> IncrementalLineSet<I>
fn clone(&self) -> IncrementalLineSet<I>
Returns a copy of the value. Read more
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source
. Read moresource§impl<I: ChtInt> Debug for IncrementalLineSet<I>
impl<I: ChtInt> Debug for IncrementalLineSet<I>
Auto Trait Implementations§
impl<I> RefUnwindSafe for IncrementalLineSet<I>where I: RefUnwindSafe,
impl<I> Send for IncrementalLineSet<I>where I: Send,
impl<I> Sync for IncrementalLineSet<I>where I: Sync,
impl<I> Unpin for IncrementalLineSet<I>
impl<I> UnwindSafe for IncrementalLineSet<I>where I: RefUnwindSafe,
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more