题目
给定[l,r],1<=l<r<=2e9,
求[l,r]内的数在二进制表示下,
0的数量大于等于1的数量 的数 的个数
思路来源
https://blog.csdn.net/libin56842/article/details/10037607
题解
注意还有i个位可以填的时候,已知j个0和k个1对后继没有影响,那就记录下来……
这题和前两天做的CF的D题的数位dp的区别在于
有个全是前导0的问题,所以dfs传参的时候多开一维,
allzero为1时代表全为前导0,即还没出现第一个1,
也就是说,比如三个全0的状态是没法转移到三个0一个1的状态的,因为三个0的状态不合法,
在出现第一个1之前,0的个数,一直只能是0,因为全是前导0
ok还是代表是否能自由取值,剩下的就都是套路了
kuangbin老师是用组合数学+非递归dp做的,留坑待补
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=35;
//dp[i][j][k] 表示还有i个数可以填 当前0的个数为j个 且1的个数为k个的时候的方案数
int bit[maxn];//把原数拆成二进制下的每一位
int dp[maxn][maxn][maxn];
//dfs(当前第pos位,当前0的个数,当前1的个数,是否自由取值,是否全是前导0)
int dfs(int pos,int num0,int num1,bool ok,bool allzero)
{
if(!pos)
{
if(allzero)return 1;
return num0>=num1;
}
if(!allzero&&ok&&~dp[pos][num0][num1])
return dp[pos][num0][num1];
int ans=0;
int mx=ok?1:bit[pos];
//有一个是否全是前导0的问题,所以得多开一维
//只有出现第一个1之后 这个数才是有效的
for(int i=0;i<=mx;++i)
{
if(allzero)
{
if(!i)ans+=dfs(pos-1,0,0,ok||(i!=mx),1);//仍然全是前导0
else ans+=dfs(pos-1,0,1,ok||(i!=mx),0);//出现第一个前导1
}
else
{
if(!i)ans+=dfs(pos-1,num0+1,num1,ok||(i!=mx),0);
else ans+=dfs(pos-1,num0,num1+1,ok||(i!=mx),0);
}
}
if(!allzero&&ok)dp[pos][num0][num1]=ans;
return ans;
}
int solve(int x)
{
bit[0]=0;
for(;x;x/=2)
bit[++bit[0]]=x%2;
return dfs(bit[0],0,0,0,1);
}
int t;
int l,r;
int main()
{
memset(dp,-1,sizeof dp);
while(~scanf("%d%d",&l,&r))
printf("%d\n",solve(r)-solve(l-1));
return 0;
}