http://www.spoj.com/problems/SORTBIT/
题意 : 问你[l, r]在这段区间里面的数字二进制下数位上1的个数排序(按从小到大, 相同的话数值小的在前)后,第k个是谁。 -2^31 + 1 <= l <= r < 2 ^ 31 - 1, 保证l, r同符号
思路 :二分 + 数位DP。 具体思路可以看下刘聪的《浅谈数位类统计问题》(第二题)。
用深搜实现代码会显得简洁、
CODE :
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lld;
const int maxn = 44;
int dp[maxn][maxn];
int bit[maxn];
int dfs(int len, int num, int fp) {
if (!num)return 1;
if (!len)return num == 0;
if (!fp && dp[len][num] != -1)return dp[len][num];
int Max = (fp ? bit[len] : 1), res = 0;
for (int i = 0; i <= Max; i++) {
res += dfs(len - 1, num - (i == 1), fp && i == Max);
}
if (!fp)dp[len][num] = res;
return res;
}
int calc(lld x, int num) {
if (x < 0)return 0;
int len = 0;
while (x) {
bit[++len] = x % 2;
x >>= 1;
}
return dfs(len, num, 1);
}
int solve(lld l, lld r, int num) {
return calc(r, num) - calc(l - 1, num);
}
lld change(lld x) {
if (x >= 0)return (1LL << 32) - x;
else return (1LL << 32) + x;
}
lld work(lld L, lld R, int k) {
int ansk, ansc, c = 0;
while (k >= 1) {
ansk = k;
k -= solve(L, R, c);//not c
ansc = c++;
}
lld l = L, r = R;
while (r >= l) {
lld mid = r + l >> 1;
if (solve(L, mid, ansc) < ansk) {
l = mid + 1;
}else {
r = mid - 1;
}
}
return r + 1;
}
int main() {
int T;
memset(dp, -1, sizeof(dp));
scanf("%d", &T);
while (T--) {
lld L, R, k, res;
scanf("%lld%lld%lld", &L, &R, &k);
if (L >= 0) {
res = work(L, R, k);
}else {
res = -change(work(change(L), change(R), k));
}
printf("%lld\n", res);
}
return 0;
}