题目传送门
-------------------上个星期的假日团队赛,这题别人的代码看了四五天才明白,来补上代码
sol1:排列组合,当考虑某一位本来是1的,现在改成了0,那么在这一位后面位的就可以随意排列组合了。
- 排列组合
#include "bits/stdc++.h" using namespace std; typedef long long LL; LL c[40][40]; // 1100 // 3210 void init() { c[0][0] = 1; for (int i = 1; i <= 31; i++) { c[i][0] = 1; for (int j = 1; j <= i; j++) c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; } } int slove(int n) { if (n == 0) return 0; int pos = 31, ans = 0; while ((n >> pos & 1) == 0) pos--; for (int i = pos - 1; i >= 1; i--) { for (int j = 0; j + 1<= (i + 1 >> 1); j++) ans += c[i][j]; } int p = 1; for (int i = pos - 1; i >= 0; i--) { if (n >> i & 1) { for (int j = 0; j + p <= (pos + 1 >> 1); j++) ans += c[i][j]; p++; } } if (p <= (pos + 1 >> 1)) ans++; return ans; } int main() { init(); int l, r; scanf("%d%d", &l, &r); printf("%d ", slove(r) - slove(l - 1)); return 0; }
sol2:数位dp,别人的代码里有一块看了好久看不懂,看懂之后觉得是冗余。。。。不过思路可学
- 数位dp
#include "bits/stdc++.h" using namespace std; int dp[32][64]; // 表示考虑到第i位,01个数差为j的方案数,因为j可能是负数统一加32 bool dig[32]; int dfs(int i, int j, bool lim, bool head) { //枚举到哪一位,sum0-sum1,限制,是否有第一个1 if (i < 1) return j == 32; if (!lim && head && ~dp[i][j]) return dp[i][j]; int up = lim ? dig[i] : 1, sum = 0; // 第i位取0的情况 if (!head) sum += dfs(i - 1, j, false, false); else sum += dfs(i - 1, j - 1, lim && dig[i] == 0, true); // 第i位取1的情况 if (up) sum += dfs(i - 1, j + 1, lim, true); if (!lim && head) dp[i][j] = sum; return sum; } int slove(int k) { int pos = 0, sum = 0; while (k) { dig[++pos] = k & 1; k >>= 1; } for (int i = 0; i <= pos; i++) sum += dfs(pos, i + 32, true, false); return sum; } int main() { int l, r; scanf("%d%d", &l, &r); memset(dp, -1, sizeof(dp)); printf("%d ", slove(r) - slove(l - 1)); return 0; }