感觉此题是P4317 花神的数论题的变形版
Description
求一段区间内二进制中 (0) 的个数不小于 (1) 的个数的数的个数
Solution
数位 DP
先考虑状态转移方程式,如何处理处所有数
设 (f[i][j][k]) 表示枚举到第 (i) 位,数字为 (j),此时二进制中 (1) 的个数为 (k)
显然有
[f[i][0][j]=sum_{k=0}^if[i-1][1][k]+f[i-1][0][k]
]
[f[i][1][j]=sum_{k=1}^if[i-1][0][k-1]+f[i-1][1][k-1]
]
与花神的数论题是一样的
另一部分,考虑答案的统计
显然,对于 (ans_{l,r}),可以转化为 (ans_{1,r}-ans_{1,l-1})
把最高位上的数与最高位以下的分开统计
由于存在原数定义的限制,我们对于 (ans_{1,V}) 可用如下方法处理
设 (len) 为 (V) 的位数, (a_i) 为 (V) 的第 (i) 位
对于首位为 (1) 且前面有 (cnt1) 个 (1) 的 (f),
(res+=f[i][0][j],iin[1,len),jin[0,len/2-cnt1])
对于首位为 0 的 f,
(res+=f[i][1][j],iin[1,len),jin[0,i/2])
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 10000100
//#define int long long
using namespace std;
int f[50][2][50],a[50];
int l,r;
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void init(){
f[1][1][1]=1;f[1][0][0]=1;
for(int i=2;i<35;i++)
for(int j=0;j<=1;j++)
for(int k=0;k<=i;k++)
for(int s=0;s<=1;s++){
if(!j)f[i][j][k]+=f[i-1][s][k];
if(j&&k) f[i][j][k]+=f[i-1][s][k-1];
}
}
int solve(int x){
memset(a,0,sizeof a);
int len=0,ans=0,cnt1=1,cnt0=0;
while(x){a[++len]=x%2;x/=2;}
for(int i=len-1;i>=1;i--){
if(a[i]) for(int j=0;j<=len/2-cnt1;j++) ans+=f[i][0][j];
cnt1+=a[i];cnt0+=(a[i]==0);
if(cnt0>=cnt1&&i==1) ans++;
}
for(int i=1;i<len;i++)
for(int j=0;j<=i/2;j++)
ans+=f[i][1][j];
return ans;
}
int main(){
init();
l=read();r=read();
printf("%d",solve(r)-solve(l-1));
return 0;
}