题意: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;
}