题解 | #毒瘤xor#
兔子的区间密码
https://ac.nowcoder.com/acm/problem/20860
不开long long见祖宗,嗯,是这样。
思路很简单,把玩几个样例过后,会发现在二进制下的l和r,只需要关心第一次出现不同的情况。(从高位往低位进发)
我的一些看法:
l:1 1 1...0 an an-1...a1
r: 1 1 1...1 bn bn-1...b1
只考虑l>r,因为l=r时候答案是0。
那么从二进制的高位往低位数,就必然会出现一位二进制位r=1,l=0的时候。
至于前面有多少个1或0我们不在乎,因为他们异或了之后都是0,没有影响。
容易发现an和bn有四种组合方式(1,0),(1,1),(0,0)(0,1)
因为l<r,所以an可以由0->1。反之,bn可以由1->0。
容易发现必然存在组合使得an和bn异或后等于1。
而对于后面的an-1和bn-1可以有同样的操作,甚至后面的数更加宽松,因为当an-1还可能可以由1->0(比如an由0->1)。所以其实an和bn是条件比较苛刻的,而后面的是更宽松的。
所以后面的每一对ai和bi都存在组合使得他们异或等于1。
好了到了这里,我们就只需要找到最高位的二进制不同的位置即可得到答案了。
只需要注意俩个点:位运算优先级的问题和开long long的问题。
代码如下:
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
ll wei(ll x)//计算r有多少位
{
int cnt = 0;
while (x)
{
x >>= 1;
cnt++;
}
return cnt;
}
inline ll read()//快读
{
ll x = 0, f = 1;
char c = getchar();
while (c<'0'||c>'9')
{
if (c == '-')f = -1;
c = getchar();
}
while ('0'<=c&&c<='9')
{
x = (x << 1) + (x << 3) + (c^48);
c = getchar();
}
return x;
}
inline void write(ll x)//快写
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)write(x/10);
putchar(x%10+'0');
}
int main()
{
ll T; T = read();
while (T--)
{
ll l = read();
ll r = read();
if (l == r){printf("0\n"); continue;}//特判相等情况
ll rr = wei(r);
int flag = 1,i=1;
ll ans = 0;
while (1)
{
ll b = r & (1LL << (rr - i));//必须开long long,不然见祖宗
ll a = l & (1LL << (rr - i));//位运算,无脑括号维护就行。
if (a!=b)//目的是找到最高位l和r不同的那个地方,也就是二进制下的r=1,l=0时候。即a!=b,因为r>l,所以这样的a和b一定存在。
{
ans = (1LL << (rr-i+1))- 1;
break;
}
i++;
}
write(ans);
puts("");
}
return 0;
}