题解 | #毒瘤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;
}
全部评论

相关推荐

点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务