传送门
题目大意,插队的问题,每个案例给出n,代表有n个插队的,每个给出p,v,意思是代号为v的人插在了第p个人的后面,问最后的队伍的排列?
题目中一开始整个队列是空的,也就是说如果输入i,j:代表代号为j的人插在了第i个人的后面,也就是说在他之前一定有了i个人,而他的位置是i+1。
离线存储,倒序更新线段树(因为后来的人好确定位置,而且先到的人的实际位置受后来的人的影响),遍历叶子结点正序输出。
与权值线段树类似的查找,查找从前往后的第k个满足条件的叶子结点。
#include <cstdio>
#include <iostream>
#define mid ((l(p) + r(p))>>1)
using namespace std;
const int N = 2e5 + 50;
struct Node {
int l, r, num, sp;
#define l(x) c[x].l
#define r(x) c[x].r
#define num(x) c[x].num
#define sp(x) c[x].sp
} c[N << 2];
int t, x[N << 2], n[N << 2];
void pushup(int p) {
sp(p) = sp(p << 1) + sp(p << 1 | 1);
}
void build(int p, int l, int r) {
l(p) = l, r(p) = r, num(p) = 0;
if (l == r) {
sp(p) = 1;
return;
}
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
void change(int p, int x, int n) {
if (l(p) == r(p) && sp(p) == 1) {
num(p) = n;
sp(p) = 0;
return;
}
if (sp(p << 1) > x) change(p << 1, x, n);
else change(p << 1 | 1, x - sp(p << 1), n);
pushup(p);
}
void print(int p) {
if (l(p) == r(p)) {
printf("%d%c", num(p), "\n "[t != 1]);
t--;
} else {
print(p << 1);
print(p << 1 | 1);
}
return;
}
int main() {
while (~scanf("%d", &t)) {
build(1, 1, t);
for (int i = 1; i <= t; i++) scanf("%d%d", &x[i], &n[i]);
for (int i = t; i; i--) change(1, x[i], n[i]);
print(1);
}
return 0;
}
更优质题解见下⬇️