C - ORXOR Editorial by ngtkana
\(a\) の添え字をひとつずらして、\(1\)-origin で書きます。
\(i \in \lbrace 0, \dots, n \rbrace\) に対して、\(a _ 0, \dots, a _ { i - 1 }\) を空でない連続した区間に分けて 、各区間内の bitwise-or を計算して、こうして得られたすべての値を bitwise-xor して得られるような値すべての集合を \(\mathtt { dp } [ i ]\) と置きます。
すると \(\mathtt { dp }\) は帰納的に
\[ \mathtt { dp } [ i ] = \begin {cases} \lbrace 0 \rbrace & i = 0, \\\\ \bigcup _ { j = 0 } ^ { i - 1 } \left \lbrace x \oplus \left ( a _ j \ \mathrm { OR } \ a _ { j - 1 } \ \mathrm { OR } \dots \mathrm { OR } \ a _ { i - 1 } \right ) \middle \vert x \in \mathtt { dp } [ i - 1 ] \right \rbrace & i \neq 0 \end {cases} \]
と計算でき、答えは \(\min \left ( \mathtt { dp } [ n ] \right )\) となります。
これは集合ですが、今回の制約では重複を除かなくてもよいです。(除いても最悪ケースでは速くなりません。)このとき \(\# \mathtt { dp } [ i ] = 2 ^ { \max ( 0, i - 1 ) }\) となり、これらの合計は \(2 ^ N\) ですから、計算量が \(\Theta \left (2 ^ N \right )\) となることがわかります。
解答例(Rust)
use itertools::Itertools;
use proconio::input;
use std::ops::BitOr;
fn main() {
input! { n: usize, a: [u32; n] }
let mut dp = vec![Vec::new(); n + 1];
dp[0].push(0);
for r in 1..=n {
for l in 0..r {
let or = a[l..r].iter().copied().fold(0, BitOr::bitor);
let xor = dp[l].iter().map(|&x| x ^ or).collect_vec();
dp[r].extend(xor);
}
}
println!("{}", dp[n].iter().min().unwrap());
}
posted:
last update: