AGC015D題解

Nekopedia發表於2024-09-18

簡要題意

給定一個區間 \([l,r]\),從中選出若干整數按位或,求可能出現的數的方案數。

資料範圍:\(1\le l\le r\le2^{60}\)

思路

首先對於 \([l,r]\) 裡的數全都滿足條件,然後因為是按位或,所以 \(l,r\) 二進位制下的一段字首就與答案無關可以先去掉。

現在我們只需要考慮比 \(r\) 還要大的數。去掉一段字首後 \(r\) 二進位制的最高位一定是 \(1\),設 \(x=highbit(r)\),我們可以根據 \(x\) 將這個區間劃分成兩部分 \([l,x)\)\([x,r]\)。對於第一個區間裡,任何數按位或答案都在第一個區間內,所以不用考慮,我們只用考慮只在第二個區間選數或者兩個區間都選數。你會發現在第一個區間選多少數都可以等價為選一個數,所以其實只用考慮選兩個數的情況。

  1. 如果只在第二個區間選數,我們可以不看最高位的一,因為他是公共部分。假設剩下的部分為 \(y\),那麼在第二個區間選數就等價於在 \([0,y]\) 內選數,設 \(z=highbit(y)\),實際上選出來的就是 \([x,x+2z)\) 的所有數,去掉小於等於 \(r\) 的答案區間就為 \((r,x+2z)\)
  2. 如果在兩個區間中各選一個數,我們可以發現對於第一個區間我們可以選 \([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;
}