-
Notifications
You must be signed in to change notification settings - Fork 230
Li chao #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Li chao #16
Conversation
Merge upstream
src/li_chao.rs
Outdated
/// midpoint, so we propagate it to that segment. This sequence ensures that the invariant is | ||
/// kept. | ||
fn add_line_impl(&mut self, mut m: i64, mut b: i64, ix: usize, l: i64, r: i64) { | ||
let x = (l + r) / 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I think you could do something like:
let (ref mut m_ix, ref mut b_ix) = self.lines[ix];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Would something like let (m_ix, b_ix) = &self.lines[ix]
also work, or is there something wrong with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you would have a typing error there because a reference to a pair is not the same as a pair of references. They have to be converted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoa never mind you're absolutely right! I was just confused because although
let (m_ix, b_ix) = &mut self.lines[ix]
compiles,
let (m_ix, b_ix): (&mut i64, &mut i64) = &mut self.lines[ix]
does not!
src/li_chao.rs
Outdated
/// beat the winner on some segment, either to the left or to the right of the current | ||
/// midpoint, so we propagate it to that segment. This sequence ensures that the invariant is | ||
/// kept. | ||
fn add_line_impl(&mut self, mut m: i64, mut b: i64, ix: usize, l: i64, r: i64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would involve some funky transformations, but I believe it's possible to use dynamic_arq::ArqView
instead of ix, l, r
if you feel so inclined :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, better yet, is it possible to just use r-l+1
nodes instead of four times that? There's no need for the subtree to again include the middle node, right? So instead, you'd start your search in the middle node, then go either left or right. Anything wrong with this idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean about including the middle node. Is there a way to get rid of some of the nodes? I think they are all needed.
The standard bound for segment tree nodes is 4N; if N is a power of 2 it's 2N but I think we need around 4N if N is slightly higher than a power of 2. That's one of the reasons the AlCash style is more efficient. That one has a bound of 2N regardless of N. But I don't know if it's easy to convert Li Chao trees to that style - they are inherently top-down, moreso than normal segment trees.
Converting to use the ArqView interface would be an interesting puzzle... I wonder if it can be done while keeping time/space complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space optimization added in latest update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay!
src/li_chao.rs
Outdated
@@ -0,0 +1,99 @@ | |||
/// A structure for answering minimum queries on a set of linear functions. Supports two |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: our other convex hull trick now searches for the max
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more. 8000
OK, I will flip everything to keep things consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the changes (min to max, some small tweaks)
It took two years, but the eagerly awaited space optimization is finally here! I validated the code on a contest training problem: https://gist.github.com/Fr0benius/9d0bf0bc4891c31f5d2f0bcaa7b9b533 |
This passes the same tests as in the sqrt decomposition implementation, but hasn't been stress tested.
Oh no, I can't seem to diff against the previous version that I reviewed! Is there a way? |
src/li_chao.rs
Outdated
@@ -0,0 +1,110 @@ | |||
/// A structure for answering maximum queries on a set of linear functions. Supports two | |||
/// operations: inserting a linear function and querying for maximum at a given point. Unlike the | |||
/// simplest convex hull trick implementation, the queries can be done in any order, and we can do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious, what's the simple implementation? I don't think the one in order.rs
has this limitation; it might be more useful to explicitly compare against that version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, it's not necessary for the nodes to be consecutive integers is it? As long as we know all the query locations (x
s) in advance... (edit: oh yeah your comment about dynamic trees sort of includes this. You might say a word about static but known x
s right before mentioning the dynamic extension.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplest version doesn't do any sqrt decomposition or anything like that, it just adds lines in increasing-slope order. I'm not as familiar with your algorithm, but I can change the wording here.
src/li_chao.rs
Outdated
std::mem::swap(&mut m, m_ix); | ||
std::mem::swap(&mut b, b_ix); | ||
} | ||
if m < self.lines[ix].0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the m_ix
variable again here?
src/li_chao.rs
Outdated
// the first i+1 lines. | ||
let results = [ | ||
[-3, -3, -3, -3, -3, -3], | ||
[-0, -1, -2, -3, -3, -3], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol what's with the negative zeros?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha I don't know, maybe I wanted it to line up with the previous line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha it almost worked, until 1
s started appearing
for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { | ||
li_chao.add_line(slope, intercept); | ||
let ys: Vec<i64> = xs.iter().map(|&x| li_chao.query(x)).collect(); | ||
assert_eq!(expected, &ys[..]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work without the [..]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it can't compare an array and a vector if you don't convert to a slice first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, it's the same as https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_slice
Cool.
src/li_chao.rs
Outdated
} | ||
|
||
/// Finds the maximum mx+b among all lines in the structure. O(log N) complexity. | ||
pub fn query(&self, x: i64) -> i64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to name the methods consistently with their equivalents in order.rs
.
We can discuss what these names should be; back then I settled on evaluate(x)
for queries and max_with(m, b)
for adding lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the changes
for (&(slope, intercept), expected) in lines.iter().zip(results.iter()) { | ||
li_chao.add_line(slope, intercept); | ||
let ys: Vec<i64> = xs.iter().map(|&x| li_chao.query(x)).collect(); | ||
assert_eq!(expected, &ys[..]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it can't compare an array and a vector if you don't convert to a slice first.
Looks great, I'm happy to merge it as is if you're done! |
Sure, sounds good to me! |
No description provided.