[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;
}