本文主要討論一下二進制表示中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; }