P4932 瀏覽器(統計二進位制1的個數)

2022-02-27 09:43:57 字數 3242 閱讀 7073

p4932 瀏覽器

有\(n\)個數,\(x_1,x_2,\cdots,x_n\),問你有多少對\((u,v)\),使得\(x_u\operatornamex_v\)的二進位制表示中有奇數個\(1\)

輸入六個整數,\(n,a,b,c,d,x_0\)。

每個點的權值需要用如下的方式生成。

\(x_i = (ax_^2 + bx_ + c) \bmod d\)

\(n\le 10^7,\max(x_i)\le 10^9,a,b,c,d,x_0\)在\(int\)範圍

位運算的題,可以先考慮運算前後的數的某些量有何關係

比如這題,可以發現,只有\(x_u,x_v\)的二進位制中\(1\)的個數一奇一偶,\(x_u\operatornamex_v\)的二進位製用才有奇數個\(1\)

可以設\(x_u,x_v\)的二進位制\(1\)有\(k\)位重合,也就是說這\(k\)位上兩個數都是\(1\)

對於異或運算,只有某一位兩數不同才是\(1\),它們有\(\text+\text-2k\)個數字不同,其實就是分別\(1\)的位數減去共同都是\(1\)的那\(k\)位

那麼即為\(\text+\text-\text=\text\)

那麼現在考慮如何快速地求乙個數二進位制中\(1\)的數量

首先有一種很顯然的方法

inline int cnt1(int x)
就是一位一位的數,複雜度\(o(\log x)\)

如果此題用這個方法,\(o(n\log x)\),算下來是\(3\cdot 10^8\),但是 1.5s 仍然跑不出最後兩個點,可能是常數過大

接下來有一種稍有優化的方法

inline int cnt2(int x)

return ret;

}

每次結果加一,然後去掉最後乙個二進位制中的\(1\)

複雜度和\(x\)二進位制中數的個數有關,最壞也是\(o(\log x)\)

對於如何去掉的最後乙個\(1\),和計算機數的儲存有關

原碼就是乙個數的二進位制表示,加上符號位,符號位是\(0\)代表整數,否則是負數

但這樣在表示負數時,每增加乙個二進位制位,數的值反而會減少,不能完成加法操作

當然也可以算它的絕對值再取符號之類的,但是對於加法這樣計算機中最基礎的運算,會顯得太麻煩

正數的反碼是其本身,負數的反碼是除了符號位,每一位取反的結果

這樣就解決了正負數各自的加法的問題,說「各自」是因為不能跨過\(0\)

因為\(+0\)表示為\(0000\),而\(-0\)表示為\(1111\),所以運算時,每跨過一次\(0\),都會使結果少一,自己舉兩個例子用反碼表示試試就知道

於是有了補碼,正數的補碼還是其本身,負數的補碼是它的反碼加一

然後就完美的解決了加法的問題

而且\(1111\)這一位就沒有數了(我們以四位二進位制數為例),所以就讓這一位表示\(-2^3\)

這也是為什麼大部分資料型別表示的範圍是\([-2^n,2^n)\)

扯完這些就能理解上面那種方法了,變成負數以後,相當於給每一位取了反,然後加一

假設這個\(x=\cdots 100\cdots\),寫出來的這個\(1\)就是最後乙個,也就是要去掉的\(1\),那麼取反以後變成\(\cdots011\cdots\)

因為取反結果後面全是\(1\),加一,都進製,就變成了\(\cdots100\cdots\)

那麼和原數做與運算,就得出了那一位\(1\),用異或去掉就行

這種方法已經能通過此題

但還有一種更妙的方法

inline int cnt3(reg int x)

inline int cnt4(reg ll x)

cnt3是處理\(int\)的,cnt4是處理\(long\space long\)的

可以看出,這種方法是\(o(\log\log x)\)

其實還有一種看起來更接近\(o(1)\),但是用到取模運算,所以真正跑起來可能每這個快,也比這個更難理解

以乙個8為二進位制數為例,\(\texttt\),其實更多位數也一樣

\(\texttt\)的二進位制是\(\texttt\)

所以,和它與,就保留了\(1,3,5,7\)位上的\(1\),就是\(\texttt\)

如果把這個二進位制數左移一位,再和它與,那麼肯定是保留了\(2,4,6,8\)為上的\(1\),然後把它分別放到了\(1,3,5,7\)位上

左移一位再與以後的結果:\(\texttt\)

和剛才那個\(\texttt\)加完以後的結果:\(\texttt\)

這裡把它兩位一斷,就能很容易的發現,對於每兩位來說,這兩位的二進位制數,就是這兩位上\(1\)的個數

然後繼續觀察,發現\(\texttt\)的二進位制是\(\texttt\)

那麼\(\texttt\operatorname\texttt=\texttt\)

這個什麼意思?當然是如果每兩位分一段的話,保留\(1,3\)段中的\(1\)

同樣,左移兩位再與,就是保留\(2,4\)段的\(1\)並放在\(1,3\)段上

再加起來,結果就是\(\texttt\)

此時,把它四位一段,前四位的二進位制數是表示前四位有多少\(1\),後四位也一樣

現在差不多就明白了,其實這個方法就是不斷把相鄰位的\(1\)的個數合併到乙個更大的區間去,最後,就是整個\(x\)表示\(x\)中\(1\)的個數

返回\(x\)

然而這個比上一種方法的總時間也就快了不到半秒

放上完整**

#include#include#include#include#include#include#define reg register

#define en std::puts("")

#define ll long long

inline int read()

while(c>='0'&&c<='9')

return y?x:-x;

}int n;

inline int cnt1(int x)

inline int cnt2(int x)

return ret;

}inline int cnt3(reg int x)

inline int cnt4(reg ll x)

int main()

std::printf("%lld",1ll*odd*even);

return 0;

}

洛谷 P4932 瀏覽器 思維題

題目大意 給你乙個序列,求滿足 x xor x 在二進位制下1的數量為奇數的數對數量 打月賽的時候真沒想出來,還是我太弱.xor意義下,對於兩個數,假設它們兩個每一位都是2個0,或者乙個1乙個0,那麼xor之後的數中1的數量是直接相加 如果有同一位有2個1,兩個1xor會變成0,1的總數量 2,也不...

二進位制檔案瀏覽器

問題及 檔名稱 one.cpp 作 者 孫金藝 完成日期 2015年6月21日 版 本 號 v1.0 問題描述 做乙個類似binaryviewer的檢視二進位制檔案的程式,輸入檔名後,可以以16進製制和ascii對照的方式列出該檔案的內容 輸入描述 略 程式輸出 略 include include ...

二進位制 二進位制中1的個數

題目 請實現乙個函式,輸入乙個整數,輸出該數二進位制表示中 1 的個數。例如,把 9 表示成二進位制是 1001,有 2 位是 1。因此,如果輸入 9,則該函式輸出 2。示例 1 輸入 00000000000000000000000000001011 輸出 3 解釋 輸入的二進位制串 0000000...