Rinne Love Xor——異或性質

題目鏈接:https://ac.nowcoder.com/acm/contest/5505/B

題解:

首先我們要分析一下這個公式

C_{i}=C_{i-1}+A_{i}\oplus B_{i}+\sum_{j=1}^{i-1}(A_{i}\oplus B_{j}+A_{j}\oplus B_{i})

舉個例子來看,假設A數組與B數組的元素個數都爲2。

C_{1}=A_{1}\oplus B_{1}

C_{2}=C_{1}+A_{2}\oplus B_{2}+A_{2}\oplus B_{1}+B_{2}\oplus A_{1} \Rightarrow C_{2}=A_{1}\oplus B_{1}+A_{2}\oplus B_{2}+A_{2}\oplus B_{1}+B_{2}\oplus A_{1}

不難發現每次求的答案就是\sum_{j=1}^{i} \sum_{k=1}^{i} A_{j}\oplus B_{k}

當然我們不能暴力,這樣時間複雜度是O(n^2)

因此我們需要考慮異或的定義:0\oplus 1 = 1           1\oplus 0 = 1          1\oplus 1 = 0              0 \oplus 0 = 1

我們把每一個數轉換成一個30位的二進制數考慮,那麼對於A數組和B數組兩個數的某一位而言,如果這兩個數的相同位上,一個爲0,一個爲1,那麼這個位就會對答案產生貢獻。

我們可以考慮記錄一下所有位上的0的個數和1的個數,

那麼對於某個數某一位而言,對答案的貢獻就是 cnt*(1<<i),i表示當前是第i位

cnt = A數組當前位上0的個數乘以B數組當前位上1的個數 + A數組當前位上1的個數乘以B數組當前位上0的個數

還是舉個例子來說明一下。我們需要維護兩個數組個A[j][0/1]和B[j][0/1]表示A數組(B數組)中第j位爲0/1的個數。

當枚舉到到第一位答案很明確就是 A_{1}\oplus B_{1},考慮每一位對答案的貢獻即可,比較容易理解。

當枚舉到第二位是,我們枚舉A2與B2的每一位,對於第0位而講,首先把A2與B2第0位的0/1的數加上去。

然後考慮A數組第0位爲1的個數(包括之前第一位累加的)與 B數組第0位爲0的的個數(包括之前第一位累加的)的乘積

相當於考慮的A1和A2第0位的0的個數和B1和B2第0位的1的個數乘積,這很顯然是正確的。

假設A1與A2的第0位都是0,B1與B2第0位都爲1,那麼對答案的貢獻就是4*(1<<i),即(1+1)*(1+1)*(1<<i)

剩下的類比一下就行了。

代碼實現(比較簡短):

#include<bits/stdc++.h>
#define rp(i,s,t) for(int i=s;i<=t;i++)
#define ll long long
using namespace std;
const ll mod = 1e9+7;
const int N = 1e5+7;
ll a[N],b[N],A[32][2],B[32][2];
int main(){
    int n;cin>>n;
    rp(i,1,n) cin>>a[i];
    rp(i,1,n) cin>>b[i];
    rp(i,1,n){
        ll res=0;
        rp(j,0,30){
            A[j][(a[i]>>j)&1]++;B[j][(b[i]>>j)&1]++;
            ll cnt=(A[j][0]*B[j][1]%mod+A[j][1]*B[j][0]%mod)%mod;
            res=(res+cnt*1ll*(1<<j)%mod)%mod;
        }
        cout<<res%mod<<((i==n)?"\n":" ");
    }
    return 0;
}

 

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