题意:
有n个的排队,每一个人都有一个val来对应,每一个后来人都会插入当前队伍的某一个位置pos。要求把队伍最后的状态输出。
解析:
逆向思维。我们可以这样考虑,最后一个人一定会得到当前队伍他想要的位置,如果我们往前一个阶段,倒数第二个人也一定能得到他想要的位置…
也就是说,我们可以这样处理,我们把最后一个人插入,然后忽略它,再把倒数第二个人插入。即,我们找出当前队伍他想要插入的位置pos的真正坐标就可以。
然后去更新整个队伍的长度。如此循环,直到最后一个人。
具体实现:
sumv[]记录该区间,目前还剩多少个空位。每一次modefy(),即插入的时候,如果该节点左儿子sumv >=pos,那么只要在左儿子找就可以了。
否则 要在右儿子中找 ,此时 pos = (pos-左儿子sumv)。
AC代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int n;
int sumv[N << 2], ans[N];
void build(int o, int L, int R) {
if(L == R) {
sumv[o] = 1;
return ;
}
int M = (L + R)/2;
build(o*2, L, M);
build(o*2+1, M+1, R);
sumv[o] = sumv[o*2] + sumv[o*2+1];
}
int p[N], v[N];
void modify(int o, int L, int R, int id) {
if(L == R) {
sumv[o]--;
ans[L] = v[id];
return ;
}
int M = (L + R)/2;
if(sumv[o*2] >= p[id]) {
modify(o*2, L, M, id);
}else {
p[id] -= sumv[o*2];
modify(o*2+1, M+1, R, id);
}
sumv[o] = sumv[o*2] + sumv[o*2+1];
}
int main() {
while(~scanf("%d", &n)) {
build(1, 1, n);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &p[i], &v[i]);
p[i]++;
}
for(int i = n; i >= 1; i--) {
modify(1, 1, n, i);
}
printf("%d", ans[1]);
for(int i = 2; i <= n; i++) {
printf(" %d", ans[i]);
}puts("");
}
return 0;
}