簡要題意
給定一個區間 \([l,r]\),從中選出若干整數按位或,求可能出現的數的方案數。
資料範圍:\(1\le l\le r\le2^{60}\)。
思路
首先對於 \([l,r]\) 裡的數全都滿足條件,然後因為是按位或,所以 \(l,r\) 二進位制下的一段字首就與答案無關可以先去掉。
現在我們只需要考慮比 \(r\) 還要大的數。去掉一段字首後 \(r\) 二進位制的最高位一定是 \(1\),設 \(x=highbit(r)\),我們可以根據 \(x\) 將這個區間劃分成兩部分 \([l,x)\) 和 \([x,r]\)。對於第一個區間裡,任何數按位或答案都在第一個區間內,所以不用考慮,我們只用考慮只在第二個區間選數或者兩個區間都選數。你會發現在第一個區間選多少數都可以等價為選一個數,所以其實只用考慮選兩個數的情況。
- 如果只在第二個區間選數,我們可以不看最高位的一,因為他是公共部分。假設剩下的部分為 \(y\),那麼在第二個區間選數就等價於在 \([0,y]\) 內選數,設 \(z=highbit(y)\),實際上選出來的就是 \([x,x+2z)\) 的所有數,去掉小於等於 \(r\) 的答案區間就為 \((r,x+2z)\);
- 如果在兩個區間中各選一個數,我們可以發現對於第一個區間我們可以選 \([l,x)\) ,當第二個區間選 \(x\) 的時候就可以湊出 \([x+l,2x)\) 中的所有數,答案區間就為 \([\max(x+l,r+1),2x)\)。
最後只需要判斷一下後兩種情況是否有交集,如果有交集那麼最後答案就直接為 \([l,2x)\),否則就把答案區間累加即可。時間複雜度只有 \(O(\log r)\),非常優秀!
程式碼
signed main(){
// fileio(fil);
l = rd(), r = rd();
if(l == r)return puts("1"), 0;
for(int i = 60; ~ i; --i){
x |= r >> i << i;
if((l >> i) ^ (r >> i))break;
}
y = x & - x; r += y - x;
l += y - x; ans = r - l + 1;
for(x = 1; x + y <= r; x <<= 1); x += y - 1;
if((y | l) <= x)return printf("%lld", (y << 1) - l), 0;
ans += (y << 1) - (y | l) + x - r;
printf("%lld", ans);
return 0;
}