|
|
## $POJ$ $3252$(组合数)
|
|
|
|
|
|
[题目传送门](http://poj.org/problem?id=3252)
|
|
|
|
|
|
### 一、题目描述
|
|
|
给出两个整数$Start,Finish$,问区间$[Start,Finish]$中 <font color='red' size=4><b>$round$ $number$</b></font>的个数。
|
|
|
($1<=Start<Finish<=2000000000$)
|
|
|
|
|
|
$round$ $number$定义:$RN$数即化为二进制后$0$的个数不少于$1$的个数的数。(<font color='blue' size=4><b>需要去除前导零</b></font>)
|
|
|
|
|
|
### 二、解题思路
|
|
|
这题的约束就是一个数的二进制中$0$的数量要不能少于$1$的数量,通过上一题,这题状态就很简单了,$dp[pos][num]$,到当前数位$pos$,$0$的数量减去$1$的数量不少于$num$的方案数,一个简单的问题,中间某个$pos$位上$num$可能为负数(这不一定是非法的,因为我还没枚举完嘛,只要最终的$num>=0$才能判合法,中途某个$pos$就不一定了),这里比较好处理,$Hash$嘛,最小就$-32$吧,直接加上$32$,把$32$当$0$用。这题主要是要想讲一下$lead$的用法,显然我要统计$0$的数量,前导零是有影响的。至于$!lead\&\&!limit$才能$dp$,都是类似的,自己慢慢体会吧。
|
|
|
|
|
|
### 三、实现代码
|
|
|
|
|
|
```c++
|
|
|
#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;
|
|
|
}
|
|
|
``` |