题目大意:在 [l,r] 区间内,找出所有的数字对 (x,y),使得 x + y = x ^ y
首先x + y = x ^ y 等价于 x & y = 0
设 s o l v e ( l , r ) solve(l,r) solve(l,r) 解决的 一个数字的范围是 [1,l],另一个数字的范围是 [1,r]。
答案可以容斥得到: s o l v e ( r , r ) − 2 ∗ s o l v e ( l − 1 , r ) + s o l v e ( l − 1 , l − 1 ) solve(r,r) - 2 * solve(l - 1,r) + solve(l - 1,l - 1) solve(r,r)−2∗solve(l−1,r)+solve(l−1,l−1)
对于 s o l v e ( l , r ) solve(l,r) solve(l,r),可以按二进制位进行 dp,只需要满足一个数小于等于 l,另一个数小于等于 r 的限制即可。这个限制在数位 dp 里很容易实现
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 10;
typedef long long ll;
int t,l,r;
int L,R;
ll dp[maxn][2][2];
ll dfs(int cur,int limit_l,int limit_r) {
if(cur < 0) return 1;
if(dp[cur][limit_l][limit_r] != -1) return dp[cur][limit_l][limit_r];
ll &ans = dp[cur][limit_l][limit_r] = 0;
int up_l = limit_l ? (L >> cur) & 1 : 1;
int up_r = limit_r ? (R >> cur) & 1 : 1;
for(int i = 0; i <= up_l; i++)
for(int j = 0; j <= up_r; j++)
if(!(i & j))
ans += dfs(cur - 1,limit_l && i == up_l,limit_r && j == up_r);
return ans;
}
ll solve(int l,int r) {
if(l < 0 || r < 0) return 0;
L = l,R = r;
memset(dp,-1,sizeof dp);
return dfs(30,1,1);
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d",&l,&r);
printf("%lld\n",solve(r,r) - 2 * solve(l - 1,r) + solve(l - 1,l - 1));
}
return 0;
}