此题应用线段树的方法非常巧妙。没做过真的难想得出是这么想的。
是一个逆向思维的运用。
其实一看到这道题目我就想到要运用逆向思维的了,但是就是没那么容易想通的。
思路:
1 要从后面往前更新线段树
2 线段树记录的是当前区间还剩下多少个记录空间
3 因为后面的插入会使得前面的插入往后退,那么前面插入的下标如果大于前面可以插入的空间,就需要往后退了。
好难理解的操作。仔细观察一下下面update这个函数吧。
二分地去查找适合的插入位置,把插入操作时间效率提高到 O(lgn),如果使用一般方法插入操作效率就会达到O(n)。
const int SIZE = 200001;
const int TREESIZE = SIZE + (SIZE<<1);
int segTree[TREESIZE];
int pos[SIZE];
int val[SIZE];
int orderVal[SIZE];
inline int lChild(int rt) { return rt<<1; }
inline int rChild(int rt) { return rt<<1 | 1; }
void build(int l, int r, int rt)
{
segTree[rt] = r - l + 1; //记录有多少个存储空间,以解决位置冲突
if (l == r) return ;
int m = l + ((r-l)>>1);
build(l, m, lChild(rt));
build(m+1, r, rChild(rt));
}
void update(int i, int p, int l, int r, int rt)
{
segTree[rt]--;
if (l == r)
{
orderVal[l] = val[i];//l为适当位置填写val
return ;
}
int m = l + ((r-l)>>1);
if (segTree[lChild(rt)] >= p) update(i, p, l, m, lChild(rt));
//下面是精要的地方,十分巧妙的插队法。如果后面有人插队了,那么前面的人和前面插队的人都需要往后推
else update(i, p - segTree[lChild(rt)], m+1, r, rChild(rt));
}
int main()
{
int n;
while(scanf("%d", &n) == 1)
{
build(1, n, 1);
for (int i = 1; i <= n; i++)
{
scanf("%d %d", &pos[i], &val[i]);
}
for (int i = n; i > 0; i--)
{
update(i, pos[i]+1, 1, n, 1);
}
for (int i = 1; i < n; i++)
{
printf("%d ", orderVal[i]);
}
printf("%d\n", orderVal[n]);
}
return 0;
}