CODE FESTIVAL 2018 qual A D - 通勤
解法
公式解説を見ながらやった。
地点 i で給油した時、残り容量は必ず f になる。x[i] + f - t までは少なくとも t 以下にはならないので、 x[i] から x[i] + f - t までの間にある給油所は、あってもなくても良い。また、iのあと給油せずにx[i] + fを超えると残量がゼロになってしまうので、x[i]+f-tからx[i]+fの間で必ず給油しなければならない。
dp[i] を i で給油する組み合わせの数とすると、ナイーブな 更新式は以下のように書ける。
for i in 0..n { let from = x.upper_bound(x[i] + f - t); let to = x.upper_bound(x[i] + f); let skip = from - 1 - i; let combinations = dp[i] * pow2[skip]; for j in from..to { dp[j] += combinations; } }
これは累積和の性質を使って更新部分を高速化出来る。
コード
use self::mod_int::ModInt; const MOD: usize = 1e9 as usize + 7; fn main() { let mut sc = Scanner::new(); let d: usize = sc.read(); let f: usize = sc.read(); let t: usize = sc.read(); let n: usize = sc.read(); let mut x: Vec<usize> = sc.read_vec(n); let mut pow2 = vec![ModInt::new(1); n + 1]; for i in 0..n { pow2[i + 1] = pow2[i] * 2; } x.insert(0, 0); x.push(d); let n = x.len(); let mut dp = vec![ModInt::new(0); n + 1]; dp[0] = ModInt::new(1); let mut sum = vec![ModInt::new(0); n + 1]; for i in 0..n { if i > 0 { sum[i] += sum[i - 1]; dp[i] = sum[i]; } let from = x.upper_bound(x[i] + f - t); let to = x.upper_bound(x[i] + f); let skip = from - 1 - i; let combinations = dp[i] * pow2[skip]; if from < dp.len() { sum[from] += combinations; } if to < dp.len() { sum[to] -= combinations; } } let mut ans = ModInt::new(0); for i in 0..(n - 1) { if x[n - 1] - x[i] <= f - t { let skip = (n - 1) - 1 - i; ans += dp[i] * pow2[skip]; } } ans += dp[n - 1]; println!("{}", ans.value); } struct Scanner { ptr: usize, length: usize, buf: Vec<u8>, small_cache: Vec<u8>, } #[allow(dead_code)] impl Scanner { fn new() -> Scanner { Scanner { ptr: 0, length: 0, buf: vec![0; 1024], small_cache: vec![0; 1024], } } fn load(&mut self) { use std::io::Read; let mut s = std::io::stdin(); self.length = s.read(&mut self.buf).unwrap(); } fn byte(&mut self) -> u8 { if self.ptr >= self.length { self.ptr = 0; self.load(); if self.length == 0 { self.buf[0] = b'\n'; self.length = 1; } } self.ptr += 1; return self.buf[self.ptr - 1]; } fn is_space(b: u8) -> bool { b == b'\n' || b == b'\r' || b == b'\t' || b == b' ' } fn read_vec<T>(&mut self, n: usize) -> Vec<T> where T: std::str::FromStr, T::Err: std::fmt::Debug, { (0..n).map(|_| self.read()).collect() } fn read<T>(&mut self) -> T where T: std::str::FromStr, T::Err: std::fmt::Debug, { let mut b = self.byte(); while Scanner::is_space(b) { b = self.byte(); } for pos in 0..self.small_cache.len() { self.small_cache[pos] = b; b = self.byte(); if Scanner::is_space(b) { return String::from_utf8_lossy(&self.small_cache[0..(pos + 1)]) .parse() .unwrap(); } } let mut v = self.small_cache.clone(); while !Scanner::is_space(b) { v.push(b); b = self.byte(); } return String::from_utf8_lossy(&v).parse().unwrap(); } } trait VecBound { fn upper_bound(&self, x: usize) -> usize; } impl VecBound for Vec<usize> { fn upper_bound(&self, x: usize) -> usize { self.binary_search_by_key(&(x * 2 + 1), |&v| v * 2) .err() .unwrap() } } pub mod mod_int { use std::ops::{Add, AddAssign, Mul, MulAssign, Rem, Sub, SubAssign}; #[derive(Copy)] pub struct ModInt<T> { pub value: T, modulo: T, } impl<T> Clone for ModInt<T> where T: Copy, { fn clone(&self) -> Self { ModInt { value: self.value, modulo: self.modulo, } } fn clone_from(&mut self, source: &ModInt<T>) { self.value = source.value; self.modulo = source.modulo; } } impl<T> Add<ModInt<T>> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { type Output = ModInt<T>; fn add(self, rhs: ModInt<T>) -> ModInt<T> { self + rhs.value } } impl<T> Add<T> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { type Output = ModInt<T>; fn add(self, rhs: T) -> ModInt<T> { let m = self.modulo; let mut t = rhs + self.value; if t >= m { t = t - m; } ModInt { value: t, modulo: self.modulo, } } } impl<T> Sub<T> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { type Output = ModInt<T>; fn sub(self, rhs: T) -> ModInt<T> { let rhs = if rhs >= self.modulo { rhs % self.modulo } else { rhs }; let value = if self.value < rhs { self.value + self.modulo } else { self.value }; ModInt { value: value - rhs, modulo: self.modulo, } } } impl<T> Sub<ModInt<T>> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { type Output = ModInt<T>; fn sub(self, rhs: ModInt<T>) -> ModInt<T> { self - rhs.value } } impl<T> AddAssign<T> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { fn add_assign(&mut self, other: T) { *self = *self + other; } } impl<T> AddAssign<ModInt<T>> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { fn add_assign(&mut self, other: ModInt<T>) { *self = *self + other; } } impl<T> SubAssign<T> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { fn sub_assign(&mut self, other: T) { *self = *self - other; } } impl<T> SubAssign<ModInt<T>> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { fn sub_assign(&mut self, other: ModInt<T>) { *self = *self - other; } } impl<T> Mul<ModInt<T>> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { type Output = ModInt<T>; fn mul(self, rhs: ModInt<T>) -> ModInt<T> { self * rhs.value } } impl<T> Mul<T> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { type Output = ModInt<T>; fn mul(self, rhs: T) -> ModInt<T> { let t = (self.value * rhs) % self.modulo; ModInt { value: t, modulo: self.modulo, } } } impl<T> MulAssign<T> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { fn mul_assign(&mut self, rhs: T) { *self = *self * rhs; } } impl<T> MulAssign<ModInt<T>> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { fn mul_assign(&mut self, rhs: ModInt<T>) { *self = *self * rhs; } } impl ModInt<usize> { pub fn new(value: usize) -> Self { ModInt { value: value, modulo: super::MOD, } } } }
AOJ 2893: Balanced Edge Deletion
解法
橋を全列挙し、それぞれについて削除したときの cost を求め、ソートすれば良い。
具体的な実装としては、
- 橋を列挙する。
- 橋を含まない連結な部分グラフを潰して 1 つの頂点とし、橋を辺とする木を作る。
- 木 DP をして、各橋を削除したときのコストを求める。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } use std::cmp; use std::collections::{BTreeMap, BTreeSet}; fn main() { input!(n: usize, m: usize, uvw: [(usize1, usize1, usize); m]); let mut graph = vec![vec![]; n]; for &(u, v, _) in uvw.iter() { graph[u].push(v); graph[v].push(u); } let mut union_find = UnionFind::new(n); let bridges = BridgeDetector::new(&graph) .bridges .iter() .map(|&(u, v)| (cmp::min(u, v), cmp::max(u, v))) .collect::<BTreeSet<_>>(); let mut edges = vec![]; for &(u, v, w) in uvw.iter() { assert!(u < v); if bridges.contains(&(u, v)) { edges.push((u, v, w)); continue; } union_find.unite(u, v); } let mut convert = BTreeMap::new(); for i in 0..n { let parent = union_find.find(i); if convert.contains_key(&parent) { continue; } let n = convert.len(); convert.insert(parent, n); } let n = convert.len(); let mut costs = vec![0; n]; for &(u, v, w) in uvw.iter() { if union_find.find(u) == union_find.find(v) { let parent = union_find.find(u); let i = *convert.get(&parent).unwrap(); costs[i] += w; } } let edges = edges .iter() .map(|&(u, v, w)| { let u = union_find.find(u); let v = union_find.find(v); assert!(u != v); let u = *convert.get(&u).unwrap(); let v = *convert.get(&v).unwrap(); (u, v, w) }).collect::<Vec<_>>(); let mut graph = vec![vec![]; n]; for &(u, v, w) in edges.iter() { graph[u].push((v, w)); graph[v].push((u, w)); } dfs(0, 0, &graph, &mut costs); let sum = uvw.iter().map(|&(_, _, w)| w).sum::<usize>(); let mut candidates = uvw .iter() .map(|&(u, v, w)| { if union_find.find(u) == union_find.find(v) { (sum - w, u, v) } else { let fu = *convert.get(&union_find.find(u)).unwrap(); let fv = *convert.get(&union_find.find(v)).unwrap(); let cost = cmp::min(costs[fu], costs[fv]); let other = sum - cost - w; (cmp::max(other, cost) - cmp::min(other, cost), u, v) } }).collect::<Vec<_>>(); candidates.sort(); let (_, u, v) = candidates[0]; println!("{} {}", u + 1, v + 1); } fn dfs(v: usize, parent: usize, graph: &Vec<Vec<(usize, usize)>>, costs: &mut Vec<usize>) -> usize { for &(child, w) in graph[v].iter() { if child != parent { let c = dfs(child, v, graph, costs); costs[v] += c + w; } } costs[v] } pub struct UnionFind { parent: Vec<usize>, sizes: Vec<usize>, size: usize, } impl UnionFind { pub fn new(n: usize) -> UnionFind { UnionFind { parent: (0..n).map(|i| i).collect::<Vec<usize>>(), sizes: vec![1; n], size: n, } } pub fn find(&mut self, x: usize) -> usize { if x == self.parent[x] { x } else { let px = self.parent[x]; self.parent[x] = self.find(px); self.parent[x] } } pub fn unite(&mut self, x: usize, y: usize) -> bool { let parent_x = self.find(x); let parent_y = self.find(y); if parent_x == parent_y { return false; } let (large, small) = if self.sizes[parent_x] < self.sizes[parent_y] { (parent_y, parent_x) } else { (parent_x, parent_y) }; self.parent[small] = large; self.sizes[large] += self.sizes[small]; self.sizes[small] = 0; self.size -= 1; return true; } } pub struct BridgeDetector { articulations: Vec<usize>, bridges: Vec<(usize, usize)>, visit: Vec<bool>, order: Vec<usize>, low_link: Vec<usize>, k: usize, } impl BridgeDetector { pub fn new(graph: &Vec<Vec<usize>>) -> Self { let n = graph.len(); let mut d = BridgeDetector { articulations: vec![], bridges: vec![], visit: vec![false; n], order: vec![0; n], low_link: vec![0; n], k: 0, }; d.run(graph); d } fn run(&mut self, graph: &Vec<Vec<usize>>) { let n = graph.len(); for i in 0..n { if !self.visit[i] { self.dfs(i, 0, graph, i); } } } fn dfs(&mut self, v: usize, previous: usize, graph: &Vec<Vec<usize>>, root: usize) { self.visit[v] = true; self.order[v] = self.k; self.k += 1; self.low_link[v] = self.order[v]; let mut is_articulation = false; let mut dimension = 0; for &next in graph[v].iter() { if !self.visit[next] { // The edge (v->next) is not a backedge. dimension += 1; self.dfs(next, v, graph, root); self.low_link[v] = cmp::min(self.low_link[v], self.low_link[next]); if v != root && self.order[v] <= self.low_link[next] { is_articulation = true; } if self.order[v] < self.low_link[next] { let min = cmp::min(v, next); let max = cmp::max(v, next); self.bridges.push((min, max)); } } else if v == root || next != previous { // The edge (v->next) is a backedge. self.low_link[v] = cmp::min(self.low_link[v], self.order[next]); } } if v == root && dimension > 1 { is_articulation = true; } if is_articulation { self.articulations.push(v); } } }
AOJ 2891: Namo.. Cut
解法
N 頂点 N 辺のグラフ(いわゆる「なもりグラフ」)は1つだけ閉路があり、その閉路に木がいくつか連結している形になる。各クエリの頂点の両方とも閉路に含まれている場合2本切断する必要があるが、そうでない場合は 1 本切断するだけで非連結に出来る。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } use std::collections::VecDeque; fn main() { input!( n: usize, uv: [(usize1, usize1); n], q: usize, ab: [(usize1, usize1); q] ); let mut graph = vec![vec![]; n]; for &(u, v) in uv.iter() { graph[u].push(v); graph[v].push(u); } let mut queue = VecDeque::new(); let mut visit_count = vec![0; n]; let mut visit = vec![false; n]; for i in 0..n { if graph[i].len() == 1 { queue.push_back(i); visit[i] = true; } } while let Some(v) = queue.pop_front() { for &next in graph[v].iter() { if visit[next] { continue; } visit_count[next] += 1; if visit_count[next] == graph[next].len() - 1 { visit[next] = true; queue.push_back(next); } } } for &(a, b) in ab.iter() { if visit[a] || visit[b] { println!("1"); } else { println!("2"); } } }
ARC094 D - Worst Case
解法
が全て より小さくなるような x、すなわち、 となるような x の最大値を求めたい。 で x は a より大きいとすると、 を満たす x を求めれば良い。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } fn main() { input!(q: usize, ab: [(usize, usize); q]); for &(a, b) in ab.iter() { let (a, b) = if a > b { (b, a) } else { (a, b) }; if a == b { println!("{}", 2 * a - 2); } else if a + 1 == b { println!("{}", 2 * a - 2); } else { println!("{}", solve2(a, b)); } } } fn solve2(a: usize, b: usize) -> usize { let mut x = (((a * b) as f64).sqrt() * 2.0) as usize - a - 1; let t = if x > a { (x - a - 1) / 2 } else { 0 }; if (x - t) * (a + 1 + t) >= a * b { x -= 1; } else if (x - (t + 1)) * (a + 1 + (t + 1)) >= a * b { x -= 1; } a - 1 + x }
CODE FESTIVAL 2018 qual B E - Game of +-
解法
を足したり引いたりしてを作る。
を足したり引いたりして 1 を作る。
にすることを目指すのではなく にすることを目指す。
であることは である。
なので、各 p について独立に考えればよいということになる。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } extern crate num_bigint; use num_bigint::BigInt; fn solve(n: usize) -> Vec<(usize, char, usize)> { let mut is_prime = vec![true; n + 1]; is_prime[0] = false; is_prime[1] = false; for i in 2..(n + 1) { if is_prime[i] { let mut cur = i * 2; while cur <= n { is_prime[cur] = false; cur += i; } } } let primes = (0..(n + 1)) .filter(|&i| is_prime[i]) .map(|p| { let mut cur = p; while cur * p <= n { cur *= p; } cur }).collect::<Vec<_>>(); let addition = primes .iter() .map(|&prime| { let mut cur = 1; for &p in primes.iter() { if p == prime { continue; } cur = (cur * p) % prime; } let add = cur; let mut cur = 0; let mut count = 0; while cur != 1 { cur = (cur + add) % prime; count += 1; } count }).collect::<Vec<_>>(); let mut ans = vec![]; let mut numerator = BigInt::from(0); let mut denominator = BigInt::from(1); let mut count = 0; for i in 0..primes.len() { let prime = primes[i]; let add = addition[i]; assert!(prime > add); let old_denominator = denominator.clone(); denominator *= prime; numerator *= prime; if add * 2 > prime { let add = prime - add; numerator -= old_denominator * add; count += add; for _ in 0..add { ans.push((1, '-', prime)); } } else { numerator += old_denominator * add; count += add; for _ in 0..add { ans.push((0, '+', prime)); } } } let zero = BigInt::from(0); while numerator < zero { numerator += denominator.clone(); count += 1; ans.push((0, '+', 1)); } while numerator > denominator.clone() { numerator -= denominator.clone(); count += 1; ans.push((1, '-', 1)); } assert_eq!(count, ans.len()); ans.sort(); ans } fn main() { input!(n: usize); if n == 1 { println!("1"); println!("+ 1"); return; } let ans = solve(n); println!("{}", ans.len()); for &(_, c, p) in ans.iter() { println!("{} {}", c, p); } }
AOJ 2900: Bumpy Array
解法
と並んでいるのを としたいが、これが成り立っていないとする。
が成り立っている時、 となっているので、 を動かすことは得にならない。よって 以降について問題を解けば良い。
が満たされていない時を考える。このとき、 の大小関係として次の 3 通りが考えられる。
1番目と2番目のケースでは、 と を入れ替えることで、 も満たされるので、そのようにする。3番目の場合では、 と を入れ替えることで、 が成り立つようになる。これら以外の入れ替え方法では2 つの不等号のうち1つしか満たされず、手数が必ず多くなる。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } use std::cmp; fn solve(mut a: Vec<i64>) -> usize { let n = a.len(); let mut ans = 0; for i in 0..(n - 1) { if i % 2 == 0 && a[i] < a[i + 1] { if i + 2 < n && a[i + 1] > a[i + 2] && a[i] > a[i + 2] { a.swap(i + 1, i + 2); } else { a.swap(i, i + 1); } ans += 1; } else if i % 2 == 1 && a[i] > a[i + 1] { if i + 2 < n && a[i + 1] < a[i + 2] && a[i] < a[i + 2] { a.swap(i + 1, i + 2); } else { a.swap(i, i + 1); } ans += 1; } } ans } fn main() { input!(n: usize, a: [i64; n]); let b: Vec<i64> = a.iter().map(|&i| -i).collect(); let a1 = solve(a); let a2 = solve(b); println!("{}", cmp::min(a1, a2)); }
AGC020 C - Median Sum
解法
解説の通り。
空でない部分列の数は 個存在するが、部分列の和が x となるような部分列の構成方法の数え上げは動的計画法で で求まる。だが、これでは間に合わない。
中央値は配列の合計の 1/2 以上になるということに気づくと、部分列の和が x となるような部分列が構成できるかどうかを判定すれば良いことになる。この DP は BitSet によって高速化出来るため、 で間に合う。すごい。
コード
/// Thank you tanakh!!! /// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 macro_rules! input { (source = $s:expr, $($r:tt)*) => { let mut iter = $s.split_whitespace(); input_inner!{iter, $($r)*} }; ($($r:tt)*) => { let mut 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_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_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::<Vec<_>>() }; ($iter:expr, chars) => { read_value!($iter, String).chars().collect::<Vec<char>>() }; ($iter:expr, usize1) => { read_value!($iter, usize) - 1 }; ($iter:expr, $t:ty) => { $iter.next().unwrap().parse::<$t>().expect("Parse error") }; } fn main() { input!(n: usize, a: [usize; n]); let sum: usize = a.iter().sum(); let mut dp = bitset::BitSet::new(sum + 1); dp.set(0, true); for i in 0..n { let pd = dp.shift_left(a[i]); dp |= pd; } for i in ((sum + 1) / 2)..(sum + 1) { if dp.get(i) { println!("{}", i); return; } } } pub mod bitset { use std::ops::{BitOrAssign, Shl}; const ONE_VALUE_LENGTH: usize = 60; const MAXIMUM: u64 = (1 << ONE_VALUE_LENGTH) - 1; pub fn get_bit_position(index: usize) -> (usize, usize) { let data_index = index / ONE_VALUE_LENGTH; let bit_index = index % ONE_VALUE_LENGTH; (data_index, bit_index) } #[derive(PartialEq, Clone, Debug)] pub struct BitSet { data: Vec<u64>, } impl BitOrAssign for BitSet { fn bitor_assign(&mut self, rhs: Self) { if self.data.len() < rhs.data.len() { self.data.resize(rhs.data.len(), 0); } let n = if self.data.len() > rhs.data.len() { rhs.data.len() } else { self.data.len() }; for i in 0..n { assert!(self.data[i] <= MAXIMUM); assert!(rhs.data[i] <= MAXIMUM); self.data[i] |= rhs.data[i]; } } } impl Shl<usize> for BitSet { type Output = Self; fn shl(self, rhs: usize) -> Self { self.shift_left(rhs) } } impl BitSet { pub fn new(n: usize) -> Self { let size = (n + ONE_VALUE_LENGTH - 1) / ONE_VALUE_LENGTH; BitSet { data: vec![0; size], } } pub fn new_from(value: u64) -> Self { BitSet { data: vec![value] } } pub fn set(&mut self, index: usize, value: bool) { let (data_index, bit_index) = get_bit_position(index); assert!(self.data.len() > data_index); if value { self.data[data_index] |= 1 << bit_index; } else { let tmp = MAXIMUM ^ (1 << bit_index); self.data[data_index] &= tmp; } } pub fn get(&mut self, index: usize) -> bool { let (data_index, bit_index) = get_bit_position(index); assert!(self.data.len() > data_index); self.data[data_index] & (1 << bit_index) != 0 } pub fn shift_left(&self, shift: usize) -> Self { let mut next_data = Vec::new(); let prefix_empty_count = shift / ONE_VALUE_LENGTH; let shift_count = shift % ONE_VALUE_LENGTH; for _ in 0..prefix_empty_count { next_data.push(0); } let mut from_previous = 0; let room = ONE_VALUE_LENGTH - shift_count; for &data in self.data.iter() { let overflow = (data >> room) << room; let rest = data - overflow; let value = (rest << shift_count) + from_previous; assert!(value <= MAXIMUM); next_data.push(value); from_previous = overflow >> room; } if from_previous > 0 { next_data.push(from_previous); } BitSet { data: next_data } } } }