题目链接:点击打开链接
题意:
给定一个区间,求区间内有多少个合法数(当这个数的二进制中0的个数>=1的个数称为合法数 二进制无前导0)
思路:
cnt[i]表示二进制长度为i位(即最高位为1,其他位任意)时的合法数个数。
sum[i] 就是二进制长度<=i位的合法数个数。
然后从最高位枚举到低位即可。维护当前0的个数。
#include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <iostream> template <class T> inline bool rd(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; while (c != '-' && (c<'0' || c>'9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } template <class T> inline void pt(T x) { if (x <0) { putchar('-'); x = -x; } if (x>9) pt(x / 10); putchar(x % 10 + '0'); } using namespace std; typedef long long ll; const int N = 100; int bit[N]; ll cnt[N], C[N][N], sum[N]; ll dfs(int len){ ll ans = sum[len - 1];//最高位(即len位一定是1)二进制长度<=len-1的合法数个数 int zero = bit[len]==0; for (int i(len-1); i; i--) { if (bit[i] == 0){ zero++; continue; } int tmp = zero + 1;//此时第i位可以填0也可以填1,若填0,则后面siz位可以任意填,填出来的数都不会大于x int siz = i - 1; if (tmp >= len - tmp)ans += 1LL << siz; else { for (int j = 0; j <= siz; j++)//同样枚举0的个数 if (tmp + j >= len - tmp - j) ans += C[siz][j]; } } ans += zero >= len - zero;//x这个数是不是合法 return ans; } ll solve(ll x){ if (x <= 1)return 1; int len = 0; for (ll tmp = x; tmp; tmp >>= 1)bit[++len] = tmp & 1; return dfs(len); } int main() { memset(C, 0, sizeof C);//计算组合数 C[1][0] = C[1][1] = 1; for (int i = 2; i < N; i++){ C[i][0] = 1; for (int j = 1; j < N; j++) C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; } cnt[1] = 1; sum[1] = 1; for (int i = 2; i < N; i++){ cnt[i] = 0;//二进制长度为i,最高位是1,其他位任意时的合法数个数 for (int j = 0; j <= i - 1; j++) //因为最高位是1,所以自由位数有i-1个,枚举其中0的个数j,若0的个数j>=1的个数i-j,则cnt[i]的方法数+=在i-1个位置中选j个0 if (j >= i - j) cnt[i] += C[i - 1][j]; sum[i] = cnt[i] + sum[i - 1]; } long long l, r; while (cin >> l >> r) cout << solve(r) - solve(l - 1) << endl; return 0; } /* 1 12 1 10 1 20 */
时间: 2024-10-12 09:13:52