poj 2828 Buy Tickets(树状数组 | 线段树)
题目大意:给定N,表示有个人,给定每一个人站入的位置,以及这个人的权值,如今按队列的顺序输出每一个人的权值。
解题思路:第K大元素,非常巧妙,将人入队的顺序倒过来看,就是纯第K大问题,然后用树状数组还是线段树就都能够做了。
C++ 线段树#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200005;
#define lson(x) ((x)<<1)
#define rson(x) (((x)<<1)+1)
struct Node {
int l, r, s;
void set(int l, int r, int s) {
this->l = l;
this->r = r;
this->s = s;
}
}nd[maxn * 4];
int N, val[maxn], pos[maxn], rec[maxn];
void build (int u, int l, int r) {
nd[u].set(l, r, r - l + 1);
if (l == r)
return ;
int mid = (l + r) / 2;
build(lson(u), l, mid);
build(rson(u), mid + 1, r);
}
int query (int u, int x) {
nd[u].s--;
if (nd[u].l == nd[u].r)
return nd[u].l;
if (nd[lson(u)].s >= x)
return query(lson(u), x);
else
return query(rson(u), x - nd[lson(u)].s);
}
int main () {
while (scanf("%d", &N) == 1) {
build(1, 0, N - 1);
for (int i = 0; i < N; i++)
scanf("%d%d", &pos[i], &val[i]);
for (int i = N - 1; i >= 0; i--)
rec[query(1, pos[i] + 1)] = i;
printf("%d", val[rec[0]]);
for (int i = 1; i < N; i++)
printf(" %d", val[rec[i]]);
printf("\n");
}
return 0;
}
C++ 树状数组#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200005;
#define lowbit(x) ((x)&(-x))
int N, fenw[maxn], pos[maxn], val[maxn], rec[maxn];
void add (int x, int v) {
while (x <= N) {
fenw[x] += v;
x += lowbit(x);
}
}
int find (int x) {
int p = 0, s = 0;
for (int i = 20; i >= 0; i--) {
p += (1<<i);
if (p > N || s + fenw[p] >= x)
p -= (1<<i);
else
s += fenw[p];
}
return p + 1;
}
int main () {
while (scanf("%d", &N) == 1) {
memset(fenw, 0, sizeof(fenw));
for (int i = 1; i <= N; i++) {
add(i, 1);
scanf("%d%d", &pos[i], &val[i]);
}
for (int i = N; i; i--) {
int tmp = find(pos[i] + 1);
rec[tmp] = i;
add(tmp, -1);
}
for (int i = 1; i <= N; i++)
printf("%d%c", val[rec[i]], i == N ? '\n' : ' ');
}
return 0;
}