You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

60 lines
2.2 KiB

2 years ago
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int N = 32;
//整数范围最大数字的二进制表示有32位也足够
//状态范围因为数字0的个数与数字1的个数的差在中间过程中可能是负数所以需要做一个偏移处理映射到自然数的下标空间也就是双倍空间足够
int f[N][N << 1];
int a[N];
int dfs(int pos, int st, bool lead, bool limit) {
if (pos == 0) return st >= 32; //大于等于偏移量32也就是count(0)-count(1)>=0,此时贡献数字1个
if (!limit && !lead && ~f[pos][st]) return f[pos][st];
int up = limit ? a[pos] : 1;
int ans = 0;
for (int i = 0; i <= up; i++) {
//前一位是前导零的情况下,本位依然是0
//(1)st不变继续传送偏移量32也就是count(0)-count(1)=0
//(2)则需要继续传递前导零标识
if (lead && i == 0)
ans += dfs(pos - 1, st, true, limit && i == a[pos]); //有前导零就不统计在内
else
//场景1前一位是前导零本位不是0
//场景2前一位不是前导零本位是0
//场景3前一位不是前导零本位不是0
//此三种场景都不需要继续传递前导零标识即lead=false
//(1)如果本位是0则传递st+1
//(2)如果本位是1则传递st-1
ans += dfs(pos - 1, st + (i == 0 ? 1 : -1), false, limit && i == a[pos]);
}
if (!limit && !lead) f[pos][st] = ans;
return ans;
}
int calc(int x) {
int al = 0;
while (x) {
a[++al] = x & 1; //取出二进制的每一位表示存入数组a中
x >>= 1;
}
// pos=al:从al位开始
// st=32:目前count(0)-count(1)=32,也就是默认加上了偏移量32
// lead=true:需要考虑前导0
// limit=true:贴上界
return dfs(al, 32, true, true);
}
int main() {
memset(f, -1, sizeof f);
// 1 ≤ Start < Finish ≤ 2,000,000,000
// INT_MAX=2147483647,看来Start和Finish还是在整数范围内
int a, b;
while (cin >> a >> b)
printf("%d\n", calc(b) - calc(a - 1));
return 0;
}