简要题意
给定一个区间 \([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;
}
标签:AGC015D,题解,2x,第二个,按位,答案,选数,区间
From: https://www.cnblogs.com/Nekopedia/p/18418778