AtCoder Beginner Contest 189
Problem A - Slot
Just check whether .
- Time complexity is .
- Space complexity is .
Code (Rust)
use proconio::input;
use proconio::marker::Chars;
fn main() {
input! {
s: Chars,
}
println!("{}", if s[0] == s[1] && s[1] == s[2] {"Won"} else {"Lost"});
}
Problem B - Alcoholic
Since Takahashi drinks in the given order, we just simulate how he drinks and check if he is drunken after drinking the current glass.
- Time complexity is .
- Space complexity is .
Code (Rust)
use proconio::input;
fn main() {
input! {
n: usize,
mut x: usize,
liquor: [(usize, usize); n],
}
x *= 100;
let mut tot = 0;
for i in 0..n {
tot += liquor[i].0 * liquor[i].1;
if tot > x {
println!("{}", i + 1);
return;
}
}
println!("-1");
}
Problem C - Mandarin Orange
This is a classical problem. It is the same as LC84 - Largest Rectangle in Histogram. We can solve this problem using a monotonic stack.
In the stack, we store heights and their starting positions. We keep the elements in the stack in an ascending order, that is to say, the largest height is at the top.
Now when we have a new height at position , we pop from the stack until the stack is empty, or the largest height is smaller than the current height. During the process, we update our answer (each popped height can extend to as far as ), and also record the leftmost position. Then we push the current height along with the leftmost position into the stack.
To avoid handling borders, we can add a at the end of the array.
- Time complexity is .
- Space complexity is .
Code (Rust)
use proconio::input;
use std::collections::VecDeque;
fn main() {
input! {
n: usize,
mut a: [usize; n],
}
a.push(0);
let mut ans = 0;
let mut dq: VecDeque<(usize, usize)> = VecDeque::new();
for i in 0..=n {
let mut nl = i;
while !dq.is_empty() && dq.back().unwrap().0 > a[i] {
let (h, l) = *dq.back().unwrap();
ans = ans.max(h * (i - l));
dq.pop_back();
nl = l;
}
if dq.is_empty() || dq.back().unwrap().0 < a[i] {
dq.push_back((a[i], nl));
}
}
println!("{}", ans);
}
Problem D - Logical Expression
This is a very simple dynamic programming problem. We just store two values, for the number of ways to get false
and for the number of ways to get true
. The transition is
- Time complexity is .
- Space complexity is .
Code (Rust)
use proconio::input;
fn main() {
input! {
n: usize,
s: [String; n],
}
let mut f = 1u64;
let mut t = 1u64;
for si in s {
if si == "AND" {
let nf = f * 2 + t;
let nt = t;
f = nf;
t = nt;
} else {
let nf = f;
let nt = t * 2 + f;
f = nf;
t = nt;
}
}
println!("{}", t);
}
Problem E - Rotate and Flip
It is natural to think of sorting the queries first, according to their timestamps.
Now let's look at the operations. Suppose we have :
- Operation I changes it to
- Operation II changes it to
- Operation III changes it to
- Operation IV changes it to
Since the operations are applied to all points at the same time, we will not calculate the position of each point, instead, we want to know the final transform.
Operation I & II swaps and , while changing the sign of one axis. Operation III & IV flips one axis and then adds to it. To handle all the operations, we will use five variables, denoting if and are swapped, denoting the sign of the current axis, denoting the sign of the current axis, denoting the extra value for and denoting the extra value for .
Now we have:
- Operation I: is flipped, and is swapped, and is swapped, then and are flipped.
- Operation II: is flipped, and is swapped, and is swapped, then and are flipped.
- Operation III: is flipped, and is flipped then added .
- Operation IV: is flipped, and is flipped then added .
For each query, after applying enough operations, we can answer the query according to the five variables:
If is
false
, the answer will beIf is
true
, the answer will beTime complexity is .
Space complexity is .
Code (Rust)
use proconio::input;
use proconio::marker::Usize1;
use std::cmp::Ordering;
fn main() {
input! {
n: usize,
points: [(i64, i64); n],
m: usize,
}
let mut ops: Vec<(usize, i64)> = vec![];
for _i in 0..m {
input! {
t: usize,
}
if t <= 2 {
ops.push((t, 0));
} else {
input! {
p: i64,
}
ops.push((t, p));
}
}
input! {
q: usize,
queries: [(usize, Usize1); q],
}
let mut ans = vec![(0i64, 0i64); q];
let mut order: Vec<usize> = (0..q).collect();
order.sort_by(|a, b| if queries[*a].0 <= queries[*b].0 {
Ordering::Less
} else {
Ordering::Greater
});
let mut swap = false;
let mut sx = 1i64;
let mut sy = 1i64;
let mut cx = 0i64;
let mut cy = 0i64;
let mut op = 0usize;
for i in order {
let (a, b) = queries[i];
while op < a {
let (t, p) = ops[op];
match t {
1 => {
swap = !swap;
std::mem::swap(&mut cx, &mut cy);
std::mem::swap(&mut sx, &mut sy);
sy = -sy;
cy = -cy;
},
2 => {
swap = !swap;
std::mem::swap(&mut cx, &mut cy);
std::mem::swap(&mut sx, &mut sy);
sx = -sx;
cx = -cx;
},
3 => {
sx = -sx;
cx = p * 2 - cx;
},
4 => {
sy = -sy;
cy = p * 2 - cy;
},
_ => {
}
}
op += 1;
}
if swap {
ans[i] = (points[b].1 * sx + cx, points[b].0 * sy + cy);
} else {
ans[i] = (points[b].0 * sx + cx, points[b].1 * sy + cy);
}
}
for i in 0..q {
println!("{} {}", ans[i].0, ans[i].1);
}
}
Problem F - Sugoroku2
Let's first simplify this problem to the following form:
- There is a mission, our succeeding probability is , and the cost is , while our failing probability is , and the cost is .
- We will stop as long as we succeed, otherwise we keep trying.
- What is the expected value of our total cost?
The expected value of the total cost can be represented as:
It equals to:
We need to calculate two sums:
and
which is a bit harder.
Actually, we have:
So we have:
So
Then we return to the original equation and now we have:
Now we return to the original problem and try to calculate , and .
Here, if we arrive at the -th position, we succeed. If we are sent back to the -th position, we fail.
The idea is similar to LC837 - New 21 Game. We store the sum of the probability of the positions which can come to the current position, then
Of course, the broken positions will not contribute to . Also, we need to add (if is not broken) and remove (if is not broken) when .
But here we also need the expected value , which can be represented as:
The expression indicates that instead of calculating , we can calculate instead:
In a similar manner, we can calculate accumulatively. Note that the broken positions' contributes to instead of .
Since surpassing the -th position is also counted as reaching the -th position, our enumeration will stop at the -th position. For each position that can reach the -th position, we will calculate its contribution to and according to the proportion .
Finally, we can get , and , and with these values, we can calculate the final answer .
- Time complexity is .
- Space complexity is .
Code (Rust)
use proconio::input;
fn main() {
input! {
n: usize,
m: usize,
k: usize,
a: [usize; k],
}
let mut broken = vec![false; n + 1];
for i in a {
broken[i] = true;
}
let mut prob_exp = vec![0.0; n + 1];
let mut prob = vec![0.0; n + 1];
prob[0] = 1.0;
let mut prob_sum: f64 = 1.0;
let mut exp_sum: f64 = 0.0;
if m >= n {
let goal_pct = (m - n + 1) as f64 / m as f64;
prob[n] += prob[0] * goal_pct;
prob_exp[n] += (prob_exp[0] + prob[0]) * goal_pct;
}
let mut prob_exp_fail = 0.0;
for i in 1..n {
prob[i] = prob_sum / m as f64;
prob_exp[i] = prob[i] + exp_sum / m as f64;
if !broken[i] {
prob_sum += prob[i];
exp_sum += prob_exp[i];
if i + m >= n {
let goal_pct = (i + m - n + 1) as f64 / m as f64;
prob[n] += prob[i] * goal_pct;
prob_exp[n] += (prob_exp[i] + prob[i]) * goal_pct;
}
} else {
prob_exp_fail += prob_exp[i];
}
if i >= m && !broken[i - m] {
prob_sum -= prob[i - m];
exp_sum -= prob_exp[i - m];
}
}
if prob[n] < 1e-8 {
println!("-1");
} else if (prob[n] - 1.0).abs() < 1e-8 {
println!("{}", prob_exp[n]);
} else {
let exp_fail = prob_exp_fail / (1.0 - prob[n]);
println!("{}", prob_exp[n] + (1.0 - prob[n]) * (prob_exp[n] + exp_fail) / prob[n]);
}
}