Codeforces——1322B.Present

本文首發於我的blog,歡迎點擊查看(無廣告界面清爽!)

題目網址: https://codeforces.com/contest/1322/problem/B

寫這篇博文是因爲第一次遇到這個解法,對於我這個算法小白來說還是很新穎的。
PS.做題的時候天真的以爲是O(n)的解法,並且可以用數學做。。。

題目

給定nn個數 a1a_1, a2a_2, … , ana_n
計算其兩兩之和的異或值。

分析

首先輸入大小以看就不可以直接求(廢話)
複雜度也就是壓在O(nlogn)左右

關鍵點
我們可以把答案回到二進制,然後對二進制的每一位分開求。

原理

首先我們定義 xi=ak+ajx_i = a_k + a_j, 即 xix_i 是兩數之和。
ss 是答案,sis_i 是指從右往左答案的第 ii 個二進制位(從0開始)

我們現在假設計算 sis_i,我們已知所有 xx 的第 ii 位的01情況,那麼求 sis_i 就是對它們做異或,也就等於計算 xx 中第 ii 位是1的個數的奇偶情況。
即如果1有偶數個,那麼答案對應的位數是0,反之則是1。(簡單的異或運算原理)

Subproblem

對於結果ss,計算第 ii 位的值,即 sis_i

(等價問題)計算兩數之和xx的第 ii 位是 11 的數量。

對於這個等價問題,我們先思考一個簡單的情況:

假設現在有一個數 y<2i+1y < 2^{i+1},它的第ii位的是否位11取決於它是否在 [2i,2i+1)[2^{i}, 2^{i+1}) 內。
現在把 yy 擴大爲正數,我們可以得到其第ii位的情況與 $y’ = y \% 2^{i+1} $。

因此,對於每個 xx,我們可以只考慮 $x \% 2^{i+1} $。

但我們希望能通過 aa 直接計算出個數,而非計算 xx 後求出解。
既然我們可以對 xx 取模,自然也可以對 aa 取模,這對加法並不會有影響。

因此,我們首先將 aa2i+12^{i+1} 取模,令取模後的數爲 bb
我們可以得到 b[0,2i+1)b \in [0, 2^{i+1} )
即有 x[0,2i+21)x \in [0, 2^{i+2}-1 )

我們發現 xx 的範圍縮小了很多,這次我們不對 xx 取模,
相反,我們可以直接對 xx 劃出兩個範圍:[2i,2i+1)[2^i, 2^{i+1})[2i+2i+1,2i+21)[2^i + 2^{i+1}, 2^{i+2}-1 )

因此,我們希望尋找 xx 在這兩個範圍內的個數

Subsubproblem

對於結果 ss 的第 ii 位,現有 bb 是對 aa 對於 2i+12^{i+1} 取模,求 bb 兩兩之和在 [2i,2i+1)[2^i, 2^{i+1})[2i+2i+1,2i+21)[2^i + 2^{i+1}, 2^{i+2}-1 ) 的個數。

(簡化版)給定數組 bb,求其兩兩和在某一區間的個數。

這裏爲簡化,假設求的區間是 [k,t)[k, t)

暫時只有 O(nlogn)O(nlogn) 的解法,未知是否有更迅速的

首先將 bb 從小到大排序,
然後對 bib_i ,它可以相加的數爲 bi+1b_{i+1}, …, bnb_n
用二分查找尋找下界值爲 kbik-b_i 的下標 pp
用二分查找尋找上界值爲 tbit-b_i 的下標 qq
滿足的個數就是 qpq-p

代碼

#include <bits/stdc++.h>

using namespace std;

long long a[400005], b[400005];

int main()
{
    ios::sync_with_stdio(false);
    long long n, i, j, k, t, m, p, q, s;
    cin >> n;
    m = 0;
    for (i = 0; i < n; i++) {
        cin >> a[i];
        m = max(a[i], m);
    }
    m = log2(m) + 2;
    s = 0;
    for (i = 0; i <= m; i++) {
        k = pow(2, i+1);
        for (j = 0; j < n; j++) {
            b[j] = a[j] % k;
        }
        sort(b, b+n);

        t = 0;
        for (j = 0; j < n; j++) {
            // 2^i, 2^(i+1)
            p = lower_bound(b+j+1, b+n, k/2 - b[j]) - b;
            q = lower_bound(b+j+1, b+n, k - b[j]) - b;
            t += q - p;

            // 2^i + 2^(i+1), 2^(i+2)-1
            p = lower_bound(b+j+1, b+n, k/2*3 - b[j]) - b;
            q = lower_bound(b+j+1, b+n, 2*k - 1) - b;
            t += q - p;
        }
        if (t % 2 == 1) {
            s += k / 2;
        }
    }
    cout << s << endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章