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...