1. 程式人生 > >二進位制表示中1的個數與異或關係

二進位制表示中1的個數與異或關係

本文主要討論一下二進位制表示中1的個數和異或的關係,本文各種結論的證明都會省去,方便記憶。

問題:給定兩個數a,b,判斷a^b在二進位制表示下1的個數的奇偶性。

分析:設a在二進位制表示下1的個數為x,b在二進位制表示下1的個數為y,a中0匹配了b中k個1.(最後一句話可能有誤,不過不影響判斷奇偶性).

故結論為:a^b中1的個數為2*k-y+x,可見a^b中1的個數的奇偶性只與x和y有關,觀察可得,如果x,y中一奇一偶,那麼答案就是奇數。

 

問題強化版:求在1---->n中任選a,b(a!=b),使得a^b在二進位制表示下1的個數為奇數,求a,b的配對方案數,a^b與b^a算是一種情況。(n<=1e7)

分析:由上分析可得,我們可以在O(1)的情況下判斷a^b在二進位制表示下1的個數的奇偶性,那麼如何求出a^b在二進位制表示下1的個數呢?

首先提供O(log(n))的方法:

    ll tot=0;(long long int)
    while(w){
        if(w&1)++tot;
        w>>=1;    
    }

這種方法是每次判斷末尾為1還是0,但是我們得求出每個數字的1的個數,在1e7的範圍下,O(nlog(n))的方法顯然不夠用,我們需要一種O(1)求個數的方法,我這裡提供兩種方法(程式碼參考部落格https://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html):

模板1:

int BitCount4(unsigned int n) 
{ 
    n = (n &0x55555555) + ((n >>1) &0x55555555) ; 
    n = (n &0x33333333) + ((n >>2) &0x33333333) ; 
    n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; 
    n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; 
    n = (n &0x0000ffff
) + ((n >>16) &0x0000ffff) ; return n ; }

模板2:

int BitCount5(unsigned int n) 
{
    unsigned int tmp = n - ((n >>1) &033333333333) - ((n >>2) &011111111111);
    return ((tmp + (tmp >>3)) &030707070707) %63;
}

感覺都好記,根據個人愛好選一種吧!

 

回到加強版的題目,現在問題只在於如何列舉a,b,其實我們並不需要列舉a,b,

只需要知道一個在(二進位制表示下1的個數為奇數的數)和(1的個數為偶數的數)是可以貢獻答案的,奇可以和偶配對,所以我們只需要求出 tot1=奇數數量 和 tot2=偶數數量,貢獻即為 tot1*tot2即可。

 

下面給個例題:

洛谷10月月賽2T1連結//這道題...打月賽的時候忘記取模

 

程式碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define R register
#define ll long long int 
using namespace std;
ll n,a,b,c,d;
ll ans,x,w,odd,even;
inline ll getx(){
     return (((a*x)%d*x)%d+b*x%d+c)%d;
}
inline ll get1(R ll k){
    R ll tot=k-((k>>1)&033333333333)-((k>>2)&011111111111);
    return ((tot+(tot>>3))&030707070707)%63;
}
int main(){
    scanf("%lld%lld%lld%lld%lld%lld",&n,&a,&b,&c,&d,&x);
    for(R ll i=1;i<=n;++i){
    x=getx();w=get1(x);
    if(w%2==1)++odd;
    else ++even;
    }
    printf("%lld",1LL*odd*even);
    return 0;
}