|
|
#include <iostream>
|
|
|
#include <algorithm>
|
|
|
#include <cstdio>
|
|
|
#include <cstring>
|
|
|
using namespace std;
|
|
|
const int N = 200010;
|
|
|
|
|
|
int n, res[N];
|
|
|
int p[N], v[N]; // 在p[i]的后面,v[i]:权值
|
|
|
|
|
|
struct Node {
|
|
|
int l, r;
|
|
|
int sum; // 此区间内空位置的数量
|
|
|
} tr[N << 2];
|
|
|
|
|
|
void pushup(int u) {
|
|
|
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
|
|
|
}
|
|
|
void build(int u, int l, int r) {
|
|
|
tr[u].l = l, tr[u].r = r;
|
|
|
if (l == r) {
|
|
|
tr[u].sum = 1; // 每个叶子节点初始化为1,表示此区间内空位置的数量是1
|
|
|
return;
|
|
|
}
|
|
|
int mid = (l + r) >> 1;
|
|
|
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
|
|
|
pushup(u); // 叶子有初始值1,所以这里需要向上更新信息
|
|
|
}
|
|
|
|
|
|
// 此处的x,是指在第x个空白位置上,不是普通前缀和的第x个位置的概念
|
|
|
void modify(int u, int x, int v) {
|
|
|
if (tr[u].l == tr[u].r) {
|
|
|
tr[u].sum = 0; // 此点被占用了,区间内空位置数量修改为0
|
|
|
// 注意:因为是魔改的线段树区间和,所以tr[u].l=tr[u].r≠x
|
|
|
// x含义:第x个空白位置
|
|
|
res[tr[u].l] = v; // 用结果数组记录最后此位置上是v这个权值
|
|
|
return;
|
|
|
}
|
|
|
// 下面也是魔改的关键部分:
|
|
|
// 如果修改的位置在左侧(左侧空白位置数>=x),递归左子树
|
|
|
if (x <= tr[u << 1].sum)
|
|
|
modify(u << 1, x, v);
|
|
|
else // 如果在右侧,递归右子树,注意 x 减去 左侧的空白数量
|
|
|
modify(u << 1 | 1, x - tr[u << 1].sum, v);
|
|
|
|
|
|
// 更新父节点信息
|
|
|
pushup(u);
|
|
|
}
|
|
|
/*
|
|
|
参考答案:
|
|
|
77 33 69 51
|
|
|
31492 20523 3890 19243
|
|
|
*/
|
|
|
int main() {
|
|
|
#ifndef ONLINE_JUDGE
|
|
|
freopen("POJ2828.in", "r", stdin);
|
|
|
#endif
|
|
|
// 加快读入
|
|
|
ios::sync_with_stdio(false), cin.tie(0);
|
|
|
while (cin >> n) {
|
|
|
build(1, 1, n); // 构建一个叶子节点值为1的线段树,描述此区间内空位置的数量
|
|
|
|
|
|
// p[i]:在p[i]的后面,即p[i]+1的位置上
|
|
|
// v[i]: 权值
|
|
|
for (int i = 1; i <= n; i++) cin >> p[i] >> v[i];
|
|
|
|
|
|
// 倒序枚举,以终为始,这样才不会破坏每个人的位置相对信息概念
|
|
|
for (int i = n; i; i--) modify(1, p[i] + 1, v[i]);
|
|
|
|
|
|
// 输出
|
|
|
for (int i = 1; i <= n; i++) printf("%d ", res[i]);
|
|
|
puts("");
|
|
|
}
|
|
|
return 0;
|
|
|
} |