use std::io::Write; use std::collections::*; type Map = BTreeMap; type Set = BTreeSet; type Deque = VecDeque; fn main() { input! { n: usize, d: i64, p: [(i64, i64); n], } let mut p = p; p.sort_by_key(|p| p.1); let mut s = vec![0; n + 1]; for i in (0..n).rev() { s[i] = s[i + 1] + p[i].0; } let mut memo = Map::new(); let mut seg = SegmentTreePURQ::new(n, 0, |a, b| *a + *b); for (i, p) in p.iter().enumerate() { memo.entry(p.1 % d).or_insert(vec![]).push(i); seg.update(i, (p.1 + d - 1) / d * p.0); } let mut ans = p[n - 1].1; ans = ans.min(p.iter().map(|p| (p.1 + d - 1) / d * p.0).sum::()); for (rem, pos) in memo { for x in pos { let p = p[x]; seg.update(x, p.1 / d * p.0); } let mut l = 0; let mut r = 10i64.pow(9) * 2; while r - l > 1 { let m = (l + r) / 2; let x = p.upper_bound_by_key(&(m * d + rem), |p| p.1); if d > s[x] { r = m; } else { l = m; } } for &r in [l, r].iter() { let k = d * r + rem; let x = p.upper_bound_by_key(&(r * d + rem), |p| p.1); ans = ans.min(r * (d - s[x]) + rem + seg.find(x, n)); } } println!("{}", ans); } /* 全体をK回やるとして K + sum_{i, B_i >= K} A_i ceil((B_i - K) / D) Bの昇順にソート B0=0 として B{i-1}<=K<=B_iの時、上の式 K mod D として考えるべきは B_j mod D (j>=i)の値 剰余固定すると? K = qD + r qD + r + sum_{i, B_i >= qD+r} A_i ceil((B_i - qD - r) / D) sum_{i, B_i >= K} A_i (ceil((B_i - r) / D) - q) q(D - sum_{i, B_i >= K} A_i) + r + sum_{i, B_i >= K} A_i ceil((B_i - r) / D) 二分探索できる形ではある、はず 実装がわからん */ // ---------- begin input macro ---------- // reference: https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 #[macro_export] macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let s = { use std::io::Read; let mut s = String::new(); std::io::stdin().read_to_string(&mut s).unwrap(); s }; let mut iter = s.split_whitespace(); input_inner!{iter, $($r)*} }; } #[macro_export] macro_rules! input_inner { ($iter:expr) => {}; ($iter:expr, ) => {}; ($iter:expr, $var:ident : $t:tt $($r:tt)*) => { let $var = read_value!($iter, $t); input_inner!{$iter $($r)*} }; } #[macro_export] macro_rules! read_value { ($iter:expr, ( $($t:tt),* )) => { ( $(read_value!($iter, $t)),* ) }; ($iter:expr, [ $t:tt ; $len:expr ]) => { (0..$len).map(|_| read_value!($iter, $t)).collect::>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::>() }; ($iter:expr, bytes) => { read_value!($iter, String).bytes().collect::>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } // ---------- end input macro ---------- // ---------- begin super slice ---------- pub trait SuperSlice { type Item; fn lower_bound(&self, key: &Self::Item) -> usize where Self::Item: Ord; fn lower_bound_by(&self, f: F) -> usize where F: FnMut(&Self::Item) -> std::cmp::Ordering; fn lower_bound_by_key(&self, key: &K, f: F) -> usize where K: Ord, F: FnMut(&Self::Item) -> K; fn upper_bound(&self, key: &Self::Item) -> usize where Self::Item: Ord; fn upper_bound_by(&self, f: F) -> usize where F: FnMut(&Self::Item) -> std::cmp::Ordering; fn upper_bound_by_key(&self, key: &K, f: F) -> usize where K: Ord, F: FnMut(&Self::Item) -> K; fn next_permutation(&mut self) -> bool where Self::Item: Ord; fn next_permutation_by(&mut self, f: F) -> bool where F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering; fn prev_permutation(&mut self) -> bool where Self::Item: Ord; } impl SuperSlice for [T] { type Item = T; fn lower_bound(&self, key: &Self::Item) -> usize where T: Ord, { self.lower_bound_by(|p| p.cmp(key)) } fn lower_bound_by(&self, mut f: F) -> usize where F: FnMut(&Self::Item) -> std::cmp::Ordering, { self.binary_search_by(|p| f(p).then(std::cmp::Ordering::Greater)) .unwrap_err() } fn lower_bound_by_key(&self, key: &K, mut f: F) -> usize where K: Ord, F: FnMut(&Self::Item) -> K, { self.lower_bound_by(|p| f(p).cmp(key)) } fn upper_bound(&self, key: &Self::Item) -> usize where T: Ord, { self.upper_bound_by(|p| p.cmp(key)) } fn upper_bound_by(&self, mut f: F) -> usize where F: FnMut(&Self::Item) -> std::cmp::Ordering, { self.binary_search_by(|p| f(p).then(std::cmp::Ordering::Less)) .unwrap_err() } fn upper_bound_by_key(&self, key: &K, mut f: F) -> usize where K: Ord, F: FnMut(&Self::Item) -> K, { self.upper_bound_by(|p| f(p).cmp(key)) } fn next_permutation(&mut self) -> bool where T: Ord, { self.next_permutation_by(|a, b| a.cmp(b)) } fn next_permutation_by(&mut self, mut f: F) -> bool where F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering, { use std::cmp::Ordering::*; if let Some(x) = self.windows(2).rposition(|a| f(&a[0], &a[1]) == Less) { let y = self.iter().rposition(|b| f(&self[x], b) == Less).unwrap(); self.swap(x, y); self[(x + 1)..].reverse(); true } else { self.reverse(); false } } fn prev_permutation(&mut self) -> bool where T: Ord, { self.next_permutation_by(|a, b| a.cmp(b).reverse()) } } // ---------- end super slice ---------- // ---------- begin segment tree Point Update Range Query ---------- pub struct SegmentTreePURQ { n: usize, size: usize, data: Vec, e: T, op: F, } impl SegmentTreePURQ where T: Clone, F: Fn(&T, &T) -> T, { pub fn new(n: usize, e: T, op: F) -> Self { assert!(n > 0); let size = n.next_power_of_two(); let data = vec![e.clone(); 2 * size]; SegmentTreePURQ { n, size, data, e, op, } } pub fn update_tmp(&mut self, x: usize, v: T) { assert!(x < self.n); self.data[x + self.size] = v; } pub fn update_all(&mut self) { for i in (1..self.size).rev() { self.data[i] = (self.op)(&self.data[2 * i], &self.data[2 * i + 1]); } } pub fn update(&mut self, x: usize, v: T) { assert!(x < self.n); let mut x = x + self.size; self.data[x] = v; x >>= 1; while x > 0 { self.data[x] = (self.op)(&self.data[2 * x], &self.data[2 * x + 1]); x >>= 1; } } pub fn find(&self, l: usize, r: usize) -> T { assert!(l <= r && r <= self.n); if l == r { return self.e.clone(); } let mut l = self.size + l; let mut r = self.size + r; let mut x = self.e.clone(); let mut y = self.e.clone(); while l < r { if l & 1 == 1 { x = (self.op)(&x, &self.data[l]); l += 1; } if r & 1 == 1 { r -= 1; y = (self.op)(&self.data[r], &y); } l >>= 1; r >>= 1; } (self.op)(&x, &y) } pub fn max_right

(&self, l: usize, f: P) -> usize where P: Fn(&T) -> bool, { assert!(l <= self.n); assert!(f(&self.e)); if l == self.n { return self.n; } let mut l = l + self.size; let mut sum = self.e.clone(); while { l >>= l.trailing_zeros(); let v = (self.op)(&sum, &self.data[l]); if !f(&v) { while l < self.size { l <<= 1; let v = (self.op)(&sum, &self.data[l]); if f(&v) { sum = v; l += 1; } } return l - self.size; } sum = v; l += 1; l.count_ones() > 1 } {} self.n } pub fn min_left

(&self, r: usize, f: P) -> usize where P: Fn(&T) -> bool, { assert!(r <= self.n); assert!(f(&self.e)); if r == 0 { return 0; } let mut r = r + self.size; let mut sum = self.e.clone(); while { r -= 1; while r > 1 && r & 1 == 1 { r >>= 1; } let v = (self.op)(&self.data[r], &sum); if !f(&v) { while r < self.size { r = 2 * r + 1; let v = (self.op)(&self.data[r], &sum); if f(&v) { sum = v; r -= 1; } } return r + 1 - self.size; } sum = v; (r & (!r + 1)) != r } {} 0 } } // ---------- end segment tree Point Update Range Query ----------