计算a^b==a+b在(l,r)的对数Codeforces Round #597 (Div. 2)

题:https://codeforces.com/contest/1245/problem/F

分析:转化为:求区间内满足a&b==0的对数(解释见代码)

///求满足a&b==0在区间【l,r】的对数 
///推导:区间[2l,2r]可由[l,r]乘3倍得来
///原因:*2我们可以看成事左移1位,那么这个位置上,对于俩个数来说 
/////////可以取0,1 或1,0或0,0才依然满足 a&b==0这个题目条件
/////////这个公式可以用递归推导回溯计算,
/////////当我们回递归到区间为奇数的情况的时候,我们想办法让它变成偶数
/////////假设[a,b]中a是奇数,那么我们就把它弄成a+1,然后加上漏算的部分即可
/////////g(x,y)表示在区间[0,b]中和x满足题目条件的个数
/////////然后漏算的部分就是 (g(a,b)-g(a,a))*2
/////////b为奇数的情况也照样这样分析
/////////

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
ll g(int a,int b){
    ll ans=0;
    ll num=0;
    for(int i=1;i<=b;i<<=1){
        if(i&b){
            b^=i;
            
            if(!(a&b))
                ans+=1ll<<num;            
        }
        if(!(i&a)){///a在二进制下i位置为0的情况下,num就是计数这种情况的 
            num++; 
        }
    }
    return ans;
}
ll cal(int a,int b){
    if(a==0)
        return 2*b-1+cal(1,b);
    if(a==b)
        return 0;
    ll ans=0;
    if(a&1){///若左区间的值为奇数 
        ans+=(g(a,b)-g(a,a))*2;///乘2是对数可以互换,然后这个就是剪掉区间缩成偶数时加上漏加的部分,类似于用前缀和算区间和 
        a++;
    }    
    if(b&1){///若右区间的值为偶数 
        ans+=(g(b-1,b)-g(b-1,a))*2;
        b--;
    }
    return ans+3*cal(a/2,b/2);
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%I64d\n",cal(a,b+1));
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/starve/p/11809208.html