文章目录
- 题目描述
- 解析
- 理解万岁!
- 代码
题目描述
解析
一寸山河一寸血
理解万岁!
首先,这题统计[l,r]的个数,可以用[1,r]-[1,l-1]来实现
接下来就是如何统计出[1,n]的个数了
首先,用dp[pos][s0]来表示一个二进制pos位有s0个0的数的个数
我们允许前导零,那么转移就非常简单:
dp[0][0]=1;
dp[pos][num]=dp[pos-1][num]+dp[pos-1][num-1];
现在就要考虑如何求了
因为有前导零,所以要分为两部分来求
具体实现看代码吧
代码
#include<bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=600;
const int M=2e6+100;
int m,n;
ll dp[50][50];
void Dp(){dp[0][0]=1;for(int pos=1;pos<=31;pos++){dp[pos][0]=1;for(int num=1;num<=pos;num++){dp[pos][num]=dp[pos-1][num]+dp[pos-1][num-1];}}
}int solve(int n){if(n==1||n==0) return 0;int ans=0,s[50],x=n,s0=0,s1=0,tot=0;while(x){s[++tot]=x&1;x /= 2;}for(int pos=tot;pos>=1;pos--){//最高位特判不要算 if(s[pos]&&tot!=pos){//有一的话:前面与n相同,pos填1 s0++;for(int i=pos-1;i>=0&&s0+i>=tot-s0-i;i--) ans+=dp[pos-1][i];s0--;}if(tot!=pos){//以pos作为最高位,填 1 for(int i=pos-1;i>=0&&i>=pos-i;i--) ans+=dp[pos-1][i];}s1+=s[pos];s0+=!s[pos];}if(s0>=s1) ans++;return ans;
}
int a,b;
int main(){Dp();scanf("%d%d",&a,&b);printf("%d\n",solve(b)-solve(a-1));return 0;
}
/*
13
100
200
1000
*/