Description
本题非常的简洁,非常的豁达,非常的粗暴。
既无所谓风雨,也无所谓天晴。
给定l,rl,r,求∑a=lr∑b=lr[a+b==a⊕b]。
其中[]里面的表达式为真时是1,否则是0。比如[2==2]=1,[2==1]=0。
Input
给定两个整数l,rl,r,保证0≤l≤r≤1e9。
Output
输出一个整数表示答案。
Sample Input 1
1 4
Sample Output 1
8
Sample Input 2
323 323
Sample Output 2
0
Sample Input 3
1 1000000
Sample Output 3
3439863766
题意: 给出边界l,r,询问[l, r]中有几对数满足a+b==a⊕b。例如[1, 4]中有(1, 2), (2, 1), (3, 4), (4, 3), (1, 4), (4, 1), (2, 4), (4, 2)这8组数满足题意。
分析: 比较明显的数位dp题目,由于题目涉及到异或运算,可以考虑把l, r拆分为二进制。类似之前做过的普通数位dp,不过之前做的只有一个上界,这道题还有一个下界。在记忆化搜索的过程中同时考虑两个数当前位取值,0-0,0-1,1-0都是符合题意的几组取值,1-1不符合题意,当搜到1-1时直接跳过即可。因为这道题只有一组数据,可以把两个数的上下界任取标记flag加入到dp数组中,节省时间,否则会TLE。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
//因为要求两个数出现的次数,普通的数位dp不好通过做差处理
int l[40], r[40];
long long dp[40][2][2][2][2];
long long dfs(int pos, bool up_flag1, bool down_flag1, bool up_flag2, bool down_flag2)//为1表示没限制
{
if(pos == 0)
return 1;
if(dp[pos][up_flag1][down_flag1][up_flag2][down_flag2] != -1)
return dp[pos][up_flag1][down_flag1][up_flag2][down_flag2];
int up1 = up_flag1 ? 1 : r[pos];
int down1 = down_flag1 ? 0 : l[pos];
int up2 = up_flag2 ? 1 : r[pos];
int down2 = down_flag2 ? 0 : l[pos];
long long ans = 0;
for(int i = down1; i <= up1; i++)
for(int j = down2; j <= up2; j++)
{
if(i == 1 && j == 1)
continue;
ans += dfs(pos-1, up_flag1||i<up1, down_flag1||i>down1, up_flag2||j<up2, down_flag2||j>down2);
}
dp[pos][up_flag1][down_flag1][up_flag2][down_flag2] = ans;
return ans;
}
long long solve(int ll, int rr)
{
int pos = 0;
for(int i = 1; i <= 30; i++)
{
l[i] = (ll>>(i-1))&1;
r[i] = (rr>>(i-1))&1;
}
return dfs(30, 0, 0, 0, 0);
}
signed main()
{
int l, r;
memset(dp, -1, sizeof dp);
cin >> l >> r;
cout << solve(l, r);
return 0;
}