[USACO06NOV] Round Numbers S
题目描述
如果一个正整数的二进制表示中, 0 0 0 的数目不小于 1 1 1 的数目,那么它就被称为「圆数」。
例如, 9 9 9 的二进制表示为 1001 1001 1001,其中有 2 2 2 个 0 0 0 与 2 2 2 个 1 1 1。因此, 9 9 9 是一个「圆数」。
请你计算,区间 [ l , r ] [l,r] [l,r] 中有多少个「圆数」。
输入格式
一行,两个整数 l , r l,r l,r。
输出格式
一行,一个整数,表示区间 [ l , r ] [l,r] [l,r] 中「圆数」的个数。
样例 #1
样例输入 #1
2 12
样例输出 #1
6
样例说明
区间 [ 2 , 12 ] [2,12] [2,12] 中共有 6 6 6 个「圆数」,分别为 2 , 4 , 8 , 9 , 10 , 12 2,4,8,9,10,12 2,4,8,9,10,12。
数据规模与约定
对于 100 % 100\% 100% 的数据, 1 ≤ l , r ≤ 2 × 1 0 9 1\le l,r\le 2\times 10^9 1≤l,r≤2×109。
原题
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[36][36][36], a[36];
int dfs(int pos, int cnt0, int cnt1, int bound, int st) // pos为此时的位置,sum代表此时的数位和,bound判断是否前面所有数都取到了上界,st判断是否前面全为0
{
if (pos == 0) // 枚举完每一位时返回
return cnt0 >= cnt1; // 0的个数不小于1的个数则计数
if (!bound && dp[pos][cnt0][cnt1] != -1) // 数位dp,可沿用先前处理过的状态的答案,从而降低复杂度
return dp[pos][cnt0][cnt1];
int max_num; // 可枚举的该位的数的上界
if (bound)
max_num = a[pos];
else
max_num = 1;
int res = 0; // 统计此时的答案
for (int i = 0; i <= max_num; i++)
{
if (st && i == 0) // 如果该位填充的0属于前导0,则cnt0和cnt1都不会增加
res += dfs(pos - 1, 0, 0, bound && (i == a[pos]), 1);
else
{
// 该位填充0则cnt0加1,填充1则cnt1加1
if (i == 0)
res += dfs(pos - 1, cnt0 + 1, cnt1, bound && (i == a[pos]), 0);
else
res += dfs(pos - 1, cnt0, cnt1 + 1, bound && (i == a[pos]), 0);
}
}
if (!bound && !st) // 没在边界时,记录下该状态对应的答案
dp[pos][cnt0][cnt1] = res;
return res;
}
int solve(ll x)
{
memset(dp, -1, sizeof(dp)); // 将dp数组初始化为-1,表示对应状态的答案目前还未计算出
int len = 0;
while (x) // 存储该数的二进制
{
a[++len] = x % 2;
x /= 2;
}
return dfs(len, 0, 0, 1, 1);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int l, r;
cin >> l >> r;
cout << solve(r) - solve(l - 1) << '\n'; // ans[l,r]=ans[0,r]-ans[0,l-1]
return 0;
}