一蓑烟雨任平生(数位DP)

博客介绍了如何使用数位动态规划(DP)解决一个特殊的数学问题,即寻找在一定范围内所有使得两数之和与两数之异或相等的整数对。文章强调了在处理此类问题时,不能简单地用前缀和来减去后缀和,因为这样会丢失部分边界情况。解决方案是通过维护四个状态的DP数组,分别表示当前数位的上下界限制,从而确保计算结果的准确性。最后,给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

题意:OnlineJudge

分析:这个题目与一般的数位DP题目还有一些不同,比如这个题中需要同时对两个数进行数位DP,而且我们分析一下容易发现,这道题目并不能简单地用solve(r)-solve(l-1)来做,这是为什么呢?原因是因为我们在solve(l-1)中求出的满足方案的a和b都是小于等于l-1的,但是我们在solve(r)中求出来的a和b有多种情况,一种是a和b均小于等于l-1(这一部分正是我们想要减去的),还有一种是a和b均大于l-1且小于r(这一部分也正是我们想要求的),但是还多出两部分,一种是a小于等于l-1,b大于l-1,另一种就是a大于l-1,而b小于等于l-1,所以说我们不能简单地认为答案就是solve(r)-solve(l-1)

所以我们就要写一个solve(l,r)函数了,严格保证a和b所选数在l和r之间,所以这个时候不仅仅需要保存上界,还需要保存下界,又因为我们需要同时对两个数进行数位DP,所以我们应该记录四个限制,分别为a的上下界和b的上下界,这样的话我们就能解决本题了

最后一点需要说明的是由于a和b之间相互影响,所以我们最好把a和b的上下界限制情况加入f数组中加以表示,否则会TLE,但是一般对一个数进行数位DP时则不需要把限制加入f数组,这一点需要重视。

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=43;
ll f[N][2][2][2][2];//f[i][0/1][0/1][0/1][0/1]表示遍历到第i位且a和b的上下限情况分别为限制和不限制的合法方案
ll l[N],r[N];
ll dp(int pos,int limit_downa,int limit_downb,int limit_upa,int limit_upb)
{
	if(!pos) return 1;
	if(f[pos][limit_downa][limit_downb][limit_upa][limit_upb]!=-1) return f[pos][limit_downa][limit_downb][limit_upa][limit_upb];
	int downa=limit_downa?l[pos]:0,downb=limit_downb?l[pos]:0,upa=limit_upa?r[pos]:1,upb=limit_upb?r[pos]:1;
	ll ans=0;
	for(int i=downa;i<=upa;i++)
	for(int j=downb;j<=upb;j++)
		if((i^j)==(i+j))
			ans+=dp(pos-1,limit_downa&&(i==downa),limit_downb&&(j==downb),limit_upa&&(i==upa),limit_upb&&(j==upb));
	f[pos][limit_downa][limit_downb][limit_upa][limit_upb]=ans;
	return ans;
}
ll solve(ll x,ll y)
{
	int pos=0;
	while(x)
	{
		l[++pos]=x%2;
		x/=2;
	}
	pos=0;
	while(y)
	{
		r[++pos]=y%2;
		y/=2;
	}
	return dp(pos,1,1,1,1);
}
int main()
{
	memset(f,-1,sizeof f);
	ll ll,rr;
	cin>>ll>>rr;
	cout<<solve(ll,rr);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值