算法竞赛进阶指南,261页, 树状数组,二分查找
题目意思:
有 n 个人,每个人有一个权值,初始队列没有人,这 n 个人进行 n 次插队,每次插队代表一个权值为 val 的人插入第 x 个位置后。
最后从前往后输出每个人的权值。 题目类似于 POJ 2182
本题要点:
1、 虽然一个人现在插在 x 位置后,但是它最终的位置不一定是 x+1 ,再后来人的插入后可能位置还会变。
但是,最后一个人插在 x 位置,这个人最后就是在 x+1 位置上;倒数第二个人,只用将最后一个人的位置"留空",
再找第 x 个没有"留空"的位置插入即可。
2、 初始化一个数组b, 每个位置要么是0,要么是1; 用树状数组来维护数组b,
然后从后往前扫描数组a:
1)寻找数组b中第 p[i].pos 个1的下标 idx, 这个下标就是第i人的排队的位置
2)将数组b的第 idx 个1置零。实际就是 树状数组 update(idx, -1);
3、 用二分查找来寻找数组b中第 p[i].pos 个1的下标
#include <cstdio>
#include <cstring>
#include <iostream>
#define lowbit(i) ((i)&(-i))
using namespace std;
const int MaxN = 200010;
int a[MaxN];
//int b[MaxN]; //计算逻辑上的b数组,存放的是 01 序列
int c[MaxN];
int ans[MaxN];
int n;struct Person
{
int pos, val;
}p[MaxN];int getSum(int k)
{
int res = 0;while(k){
res += c[k];k ^= lowbit(k); //这里使用 异或来代替 k -= lowbit(k), 计算速度快一点}return res;
}void update(int x, int v)
{
for(int i = x; i <= n; i += lowbit(i)){
c[i] += v; }
}bool judge(int mid, int s)
{
return getSum(mid) >= s;
}int find_index(int s)
{
int L = 1, R = n + 1, mid;while(L < R){
mid = (L + R) / 2; if(judge(mid, s)){
R = mid;}else{
L = mid + 1;}}return L;
}void solve()
{
memset(c, 0, (n + 1) * sizeof(int));for(int i = 1; i <= n; ++i){
update(i, 1); }int idx;for(int i = n; i >= 1; --i){
idx = find_index(p[i].pos);ans[idx] = p[i].val; update(idx, -1);}for(int i = 1; i < n; ++i){
printf("%d ", ans[i]);}printf("%d\n", ans[n]);
}int main()
{
while(scanf("%d", &n) != EOF){
for(int i = 1; i <= n; ++i){
scanf("%d%d", &p[i].pos, &p[i].val);p[i].pos++;}solve();}return 0;
}/* 4 0 77 1 51 1 33 2 69 4 0 20523 1 19243 1 3890 0 31492 *//* 77 33 69 51 31492 20523 3890 19243 */