🔥
競プロ解説 [ABC098D]
はじめに
本記事は、日々の精進で出会った問題の一部を丁寧目に解説することで、筆者と読者のアルゴリズム力を向上させることを目的としています。
問題
考察
次の定理が成り立ちます。
任意の非負整数
について、 a, b が成り立つ。等号は a \oplus b \le a + b のときに限り成り立つ。 a \land b = 0
定理より、
したがって、本問題の愚直な解法は次の通りです。
let mut counter = 0
for l in 0..n {
let mut r = l + 1;
let mut acc_sum = a[l];
while r < n && acc_sum & a[r] == 0 {
acc_sum += a[r];
r += 1
}
counter += r - l
}
上記のコードはこのままでは TLE
してしまいますが、少しの工夫で計算量を
while
ループは高々
回答例
use itertools::Itertools;
use proconio::input;
fn main() {
input! { n: usize, mut a: [u32; n], }
let next_nonzero = {
let mut res = (0..n)
.rev()
.scan(n, |state, i| {
let res = Some(*state);
if a[i] > 0 {
*state = i
}
res
})
.collect_vec();
res.reverse();
res
};
let mut res = 0;
for l in 0..n {
let mut r = next_nonzero[l];
let mut acc_sum = a[l];
// up to 20 steps
while r < n && acc_sum & a[r] == 0 {
acc_sum += a[r];
r = next_nonzero[r]
}
res += r - l
}
println!("{}", res)
}
Discussion