AtCoder Regular Contest 092 D - Two Sequences

「3月中に毎日1問解いて黄色になろう!」キャンペーン6問目のつもり。コンテストには参加していないので。でも、これ500はちとキツイ気がしています(個人的にEの方が簡単なので)

問題概要

N個の要素からなる数列AとBが与えられるので、for s,t in itertools.product(a,b)でのs+tの全xorを求めなさい。
N≦2×105、a,b<228

D - Two Sequences

解説

普通に全部生成しても間に合いません。そこで、aとbの不思議な制約に目を付けます。ただ、228はそのまま使うには時間的にもメモリ的にも無理です。となると、2進数で桁を独立に確定していく…?となります。(従属でも考えましたが、繰り上がり処理で無限に死にます)

とにかく繰り上がりで死ぬので、それを考慮する必要のない形で考えると、「2iの位を調べたいときに、Bのmod 2i+1を取り、2i-a[j]よりも大きな要素は2iが1になるんじゃね?」という発想が出ます(出ません)。なお、ここで2i要素の配列を用意したくなる(modの各結果に応じて)のですが、先述の通り時間もメモリも足りません。modを取ってソートしましょう。

具体的には、a[j]の2i位が1ならOKな範囲が2つに分かれるので、その両方を考える必要があります。0の時は2i-a[j]~2i+1-1-a[j]まで、1の時はそれに加えて3×2i-a[j]~4×2i-1-a[j]までのb[j]の個数が1となる個数で、この個数が全体で奇数なら2iは1で、そうでなければ0です。これらはlower_boundで位置を探して、イテレータの差を取ればO(logN)で算出できます。

これで、各jについてa[j]と対応する個数がO(logN)で求まり、各桁O(NlogN)で求まります。

https://beta.atcoder.jp/contests/arc092/submissions/2235555

int main() {
    int n; cin >> n;
    vector<int> a(n), b(n);
    rep(i, n)   cin >> a[i];
    rep(i, n)   cin >> b[i];
    sort(all(a));
    sort(all(b));
    int ret = 0;
    vector<int> amod(n), bmod(n);
    rep(i, 29) {
        rep(j, n)   amod[j] = a[j] % (2 << i);
        rep(j, n)   bmod[j] = b[j] % (2 << i);
        sort(all(bmod));
        int cnt = 0;
        rep(j, n) {
            if (amod[j] & (1 << i)) {
                // ooxxxxxxoo
                cnt += lower_bound(all(bmod), (2 << i) - amod[j]) - bmod.begin();
                cnt += bmod.end() - lower_bound(all(bmod), (3 << i) - amod[j]);
            }
            else {
                // xxxxoooooxx
                auto s = lower_bound(all(bmod), (1 << i) - amod[j]);
                auto t = lower_bound(all(bmod), (2 << i) - amod[j]);
                cnt += t - s;
            }
        }
        ret ^= ((cnt&1) << i);
    }
    cout << ret << endl;
    return 0;
}

感想

頭が無い。頭が欲しい。