[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。
原题
洛谷P6218——传送门
代码
#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];elsemax_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加1if (i == 0)res += dfs(pos - 1, cnt0 + 1, cnt1, bound && (i == a[pos]), 0);elseres += 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;
}