> makibishi throw|

ABC117D - XXOR

問題概要

リンク

NN個の非負整数A1,A2,ANA_1,A_2,\cdots A_NKKが与えられる。

f(X)=XA1+XA2++XANf(X) = X \oplus A_1 + X \oplus A_2 + \cdots + X \oplus A_Nとするとき、0XK0 \leq X \leq Kでのffの最大値を求めよ。

制約

0N1050 \leq N \leq 10^5

0K10120 \leq K \leq 10^{12}

0Ai10120 \leq A_i \leq 10^{12}

解法

f(X)f(X)を求めるために0XK0 \leq X \leq Kの範囲を全て計算して最も大きくなるものを求めたいが、制約上O(K)O(K)では TLE になってしまう。そこで、XX に入る値を全て調べることなく解にたどり着けるようなアプローチを考える。

XKX \leq Kを無視して考えてみる

まず、XKX \leq Kの制限がない場合、f(X)f(X)が最大になるにはXXがどのようになっていればいいか考える。f(X)f(X)の性質上、XXを 2 進数で表した時のii桁目の値は各A1,...ANA_1,...A_Nii桁目の値にしか関係がないので、各桁ごとに独立に考えることができる。

XXを 2 進数で表した時のii桁目(XiX_iと表記する)がf(X)f(X)に寄与する値fXif_{X_i}に注目すると、

fXi={Xi0A1,...ANi1×2i1Xi1A1,...ANi0×2i1f_{X_i} = \Biggl\{ \begin{array}{cc} X_iが0のとき & A_1,...A_Nのうち、 i桁目が1になっているものの数 \times 2^{i-1} \\ X_iが1のとき & A_1,...A_Nのうち、 i桁目が0になっているものの数 \times 2^{i-1} \\ \end{array}

となる。XXが 2 進数で 60 桁まで取れるとすると、f(X)=i=160fXif(X) = \sum_{i=1}^{60} f_{X_i}なので、

あらかじめ各iiについてA1,...ANA_1,...A_Nii桁目が 1 になっている個数をカウントしておけば、X のii桁目はfXif_{X_i}がより大きいものを選んでいけばいいのでO(N)O(N)で解くことができる。

XKX \leq Kを考慮する

f(X)f(X)を考えるにあたり、XX の各ビット毎に独立で考えることができることが分かったが、今回は厄介なXKX \leq Kという制約がある。この制約により、あるiiに対するXiX_iを自由に決めてよいかどうかが、ii以外の他のビットをどのように決めたかに依存してしまう。

そこで、他のビットがどうなっている状態ならXiX_i を自由に決めてよいかを考察してみる。

まず、KKii桁目(KiK_i)が 0 なら、そのビットを 1 にしたK+iK_{+i}を作った場合、明らかにK+i>KK_{+i} > KなのでX=K+X=K_{+}とできない。 一方Ki=1K_i = 1なら、そのビットを 0 にしたKiK_{-i}K>KiK > K_{-i}になるため、X=KiX=K_{-i}とすることができる。

また、Ki=1K_i = 1のときにX=KiX=K_{-i}としたとき、その後iiより下位の桁を全て 1 に変更したとしてもXKX \leq Kが保たれる。

まとめると、以下のようになる。

Xi={Ki=0ijKj=1Xj=0010Ki=101X_i = \Biggl\{ \begin{array}{cc} K_i = 0 のとき & iよりも上位のビットjで、K_j=1かつX_j=0なら 0か1、そうでないなら0 \\ K_i = 1 のとき & 0か1 \\ \end{array}

このような制約の元でXiX_iを決めるには、X=KX=Kとしたときと、Ki=1K_i=1となるビットでXi=0X_i=0として、下位ビット全ておよび上位のKj=1K_j=1となっているビットを自由に選んだものを検証すればよい。

例として、K=11001101K=11001101 としたとき、自由に選んでよいビットを?で表すと

11001101(X=K)??00??00(K1=0)??00?0??(K3=0)??000???(K4=0)?0??????(K7=0)0???????(K8=0)\begin{array}{cc} 11001101 & (X=K) \\ ??00??00 & (K_1=0) \\ ??00?0?? & (K_3=0) \\ ??000??? & (K_4=0) \\ ?0?????? & (K_7=0) \\ 0??????? & (K_8=0) \\ \end{array}

を調べて、f(X)f(X)が最大になるXiX_iを選べばよい。

Code

下記が、rust でこの問題を解いた場合のコードです。

fn solve() {
    input! {
        n: usize,
        k: usize,
        a: [usize;n]
    }

    let mut k_bit = vec![0; 60];
    for b in 0..60 {
        k_bit[b] = k >> b & 1;
    }

    let mut a_sum_bit = vec![0; 60];
    for i in 0..n {
        let a_i = a[i];
        for b in 0..60 {
            a_sum_bit[b] += a_i >> b & 1;
        }
    }

    // まずx=kの場合のf(x)を見る
    let mut ans = 0;
    for i in 0..60 {
        let base = 1 << i;
        ans += if k_bit[i] == 1 {
            (a.len() - a_sum_bit[i]) * base
        } else {
            a_sum_bit[i] * base
        }
    }

    for i in 0..60 {
        let mut tmp_ans = 0;

        // kのi+1ビット目が1なら、そのビットを0にする代わりに下位のビットは全て0か1のより大きい方として良い
        if k_bit[i] == 1 {
            for j in 0..60 {
                let base = 1 << j;
                let bit_j_0 = a_sum_bit[j] * base;
                let bit_j_1 = (a.len() - a_sum_bit[j]) * base;
                if j < i {
                    tmp_ans += cmp::max(bit_j_0, bit_j_1);
                } else if j == i {
                    tmp_ans += bit_j_0;
                } else {
                    tmp_ans += if k_bit[j] == 0 { bit_j_0 } else { bit_j_1 };
                }
            }
        }

        ans = cmp::max(tmp_ans, ans);
    }

    println!("{}", ans);
}