[hdu 5592 ZYB's Premutation] 树状数组+二分 求第K大数

[hdu 5592 ZYB's Premutation] 树状数组+二分 求第K大数

题意描述中文题面
解题思路:可以根据前缀逆序对数求出第 i 个数前面有多少个大于 这个数 的个数,以及后面有多少个数小于 这个数 的个数。令B[i] 表示 第 i 个数, 在前面i 个数中排 第几。 显然, 最后一个数  的答案就是 B[N], 接着考虑倒数第二个数,倒数第二个数就是 N 个数中, 除去 最后一个数 排 B[N - 1] 的数。
用树状数组的前缀和标记 1~N 在 答案的排列 中是第几小。初始化每个数都标记为1, 每选择一个数就将这个数清零。每次寻找第 K 小的位置,二分 树状数组就好了。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

//#pragma comment(linker, "/STACK:1024000000,1024000000")

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second

typedef __int64  LL;
//typedef long long LL;
typedef unsigned int uint;
typedef pair<int, int> PII;
typedef long double LD;

const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const int MAXN = 50000 + 5;

int T, N;
int A[MAXN], B[MAXN];
int tree[MAXN], res[MAXN];

inline int lowbit (int& x) {
    return x & (-x);
}
void update (int p, int v) {
    while (p <= N) {
        tree[p] += v;
        p += lowbit (p);
    }
}
int query (int p) {
    int ret = 0;
    while (p) {
        ret += tree[p];
        p -= lowbit (p);
    }
    return ret;
}
void init() {
    memset (tree, 0, sizeof (tree) );
    for (int i = 1; i <= N; i++) {
        update (i, 1);
    }
}
int bsearch (int v) {
    int low, high, mid;
    low = 1, high = N;
    while (low <= high) {
        mid = (low + high) >> 1;
        if (query (mid) < v) low = mid + 1;
        else high = mid - 1; /// 注意:这里求的是upper_bound
    }
    return low;
}
int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    scanf ("%d", &T);
    while (T --) {
        scanf ("%d", &N);
        A[0] = 0;
        for (int i = 1; i <= N; i++) {
            scanf ("%d", &A[i]);
            B[i] = A[i] - A[i - 1];
            B[i] = i - 1 - B[i];
        }
        init();
        for (int i = N; i >= 1; i--) {
            res[i] = bsearch (B[i] + 1);
            update (res[i], -1);
        }
        for (int i = 1, p; i <= N; i++) {
            printf ("%d%c", res[i], i == N ? '\n' : ' ');
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值