[究极好题][数位dp]一蓑烟雨任平生

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值