题目链接:POJ3252
题意很简单:【L,R】区间中,各个数的二进制表示形式中0的个数不小于1的个数的数目
本来是可以1A的简单题,超时了无数发,来解释解释细节
看到题目中L,R的大小是2*10^9,在int范围内,超时跟long long可能也有关系
然后就是状态的分析
dp【pos】【x】【y】:当前枚举到了第pos位,0的个数有x个,1的个数有y个,符合题目意义的值有多少个
由于前导0对于题目影响,所以需要一个bool型变量zero来标记当前情况下是不是前面全都是全导0,然后呢,dfs记忆化的模板也出来了
int dfs(int pos,int x,int y,bool zero,bool flag)
zero==0前面全是前导0
zero==1前面有某个1
当pos==0的时候,就需要判断这个数是不是符合条件了
第一种情况呢,就是没有前导0了,并且0的个数不少于1的个数
第二种情况呢,就是全是前导0,也就是zero=0
超时主要是在记忆化这块的:
判断能否记忆化,只与当前的flag标记有关,意思是高低位的值影响,当前有没有前导0是不影响的
if (flag&&dp[pos][x][y]!=-1) return dp[pos][x][y];
如果在这加了zero的判断的话,会浪费很多时间在不需要的计算上,当然超时
但是在记录的时候,是需要判断zero标记位的
这道题有没有zero判断不会错,但是:HDOJ3967
这个题必须判断zero,我也不明白为什么
还有1点就是转移的时候,要根据是不是前导0和当前填写的也是0来判断要不要把这次枚举的0或者1的数位值统计到x和y上
#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 1050
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
int dp[50][80][80];
int digit[50];
int dfs(int pos,int x,int y,bool zero,bool flag){
//zero==0前面全是前导0
//zero==1前面有某个1
if (pos==0) return (zero&&x>=y)||(zero==0);
if (flag&&dp[pos][x][y]!=-1) return dp[pos][x][y];
int maxnumber=flag?1:digit[pos];
int ans=0;
for(int i=0;i<=maxnumber;i++)
if (zero==0&&i==0)
ans+=dfs(pos-1,x,y,0,flag||i<maxnumber);
else
ans+=dfs(pos-1,x+1-i,y+i,zero||i,flag||i<maxnumber);
if (flag&&zero) dp[pos][x][y]=ans;
return ans;
}
int calc(int n){
if (n<0) return 0;
int len=0;
while(n){
digit[++len]=n%2;
n/=2;
}
return dfs(len,0,0,0,0);
}
int A,B;
int main(){
//input;
memset(dp,-1,sizeof(dp));
while(scanf("%d%d",&A,&B)!=EOF)
printf("%d\n",calc(B)-calc(A-1));
//printf("%I64d\n",calc(A-1));
//printf("%I64d\n",calc(B));
return 0;
}