UVALive 3675 Sorted bit sequence(數位dp+二分)
阿新 • • 發佈:2019-02-05
題目連結
題意
將區間
資料規模:
分析
參考論文中的分析方法。
首先注意到一個條件
我們先考慮
由於排序的第一關鍵字是1的數量,第二關鍵字是數的大小,因此我們很容易確定答案中1的個數:依次統計區間[m,n]內二進位制表示中含 1的數量為 0,1,2,…的數,直到累加的答案超過 dfs
。
同時,我們也求出了答案是第幾個[m,n]中含 s個 1 的數。因此,只需二分答案,求出
對於
需要特殊處理
因為
Code
//https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=245&page=show_problem&problem=1676
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long ll;
const ll base = 1ll << 32;
int T, digit[35];
ll L, R, K, LL, RR;
ll dp[35][35][35], cnt[4][35];
ll dfs(int pos, int pre, int limit, int sum)
{
if (pos == -1) return pre == sum;
if (pre > sum) return 0;
if (!limit && dp[pos][pre][sum] != -1) return dp[pos][pre][sum];
int last = limit ? digit[pos] : 1;
ll ret = 0;
for (int i = 0; i <= last; ++i) {
ret += dfs(pos - 1, pre + i, limit && (i == last), sum);
}
if (!limit) dp[pos][pre][sum] = ret;
return ret;
}
ll solve(ll x, int id, int flag)
{
memset(digit, 0, sizeof (digit));
int len = 0;
// printf("x = %lld\n", x);
while (x) {
digit[len++] = x % 2;
x /= 2;
}
if (flag != -1) {
return dfs(len - 1, 0, 1, flag);
}
memset(cnt[id], 0, sizeof(cnt[id]));
for (int i = 1; i <= 32; ++i) { // 別忘了32!
cnt[id][i] = dfs(len - 1, 0, 1, i); // 列舉有i個1
// printf("cnt[%d][%d] = %lld\n", id, i, cnt[id][i]);
}
return 0;
}
void work(int flag)
{
solve(LL, 0, -1);
solve(RR, 1, -1);
ll prefix = 0;
int goal;
for (int i = 1; i <= 32; ++i) { // 別忘了32!
prefix += (cnt[1][i] - cnt[0][i]);
if (prefix >= K) {
prefix -= (cnt[1][i] - cnt[0][i]);
goal = i;
break;
}
}
// printf("goal = %d\n", goal);
ll left = K - prefix + cnt[0][goal];
ll high = RR, low = LL, mid;
while (low < high) {
mid = (1ll * low + high) / 2;
ll tmp = solve(mid, -1, goal);
if (tmp < left) low = mid + 1;
else high = mid;
}
ll ans = high;
if (flag) ans = -(base - high);
printf("%lld\n", ans);
}
int main()
{
memset(dp, -1, sizeof(dp));
scanf("%d", &T);
while (T--) { // 注意n * m >= 0
scanf("%lld%lld%d", &L, &R, &K);
if (L < 0 && R < 0) {
LL = base + L - 1, RR = base + R;
work(1);
} else if (L < 0 && R == 0) {
if (K == 1) printf("0\n");
else {
K--;
LL = base + L - 1, RR = base - 1;
work(1);
}
} else if (L == 0 && R == 0) { // K = 0
printf("0\n");
} else if (L == 0 && R > 0) {
if (K == 1) printf("0\n");
else {
K--;
LL = 0, RR = R;
work(0);
}
} else { // L > 0 && R > 0
LL = L - 1, RR = R;
work(0);
}
}
return 0;
}