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: