AGC015D题解

简要题意

给定一个区间 \([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;
}
posted @ 2024-09-18 16:22  Lyrella  阅读(36)  评论(0)    收藏  举报