题目链接:http://poj.org/problem?id=2828
/**//*
题意:
给定N(1 <= N <= 200000)个整数对(A,B),表示在A右边的位置插入一个B,
经过N次操作后问最后的B序列的排列情况。
题解:
树状数组 或者 线段树
思路:
这题的数据量比较大,一开始可以模拟一下过程,但是直接暴力肯定是超时
的,因为每次插入过程,这个位置的后面的元素必然是要顺序往后移动的。所以
总的复杂度高达O(n^2)。
但是这个问题可以转化,我们这样考虑,对于任意两个整数对(A1,B1)和(A2,B2)
保证(A1,B1)在(A2,B2)之前出现,如果A1小于A2,后面的整数对是不影响前面整
数对的位置关系的,否则B1的位置必然要受到B2的影响而向后移动一位。
于是A1和A2之间就存在一个逆序关系,我们可以联想到树状数组求逆序数时
候的做法,从后往前,对于最后一个数,它的位置就是An,因为之后没有插入数
了,它已经稳定下来了,然后将这个位置插入到树状数组的相应位置去,每次扫
描到当前数的时候二分枚举当前数前面有多少“空位”,空位的统计可以采用树
状数组的成段求和,找到后将这个数插入,N次操作后答案就保存下来了。
*/
#include <iostream>
using namespace std;
#define maxn 200010
int n;
int c[maxn];
struct point {
int A, B;
}pt[maxn];
int lowbit(int x) {
return x & (-x);
}
void add(int pos) {
while(pos <= n) {
c[pos] ++;
pos += lowbit(pos);
}
}
int sum(int pos) {
int s = 0;
while(pos > 0) {
s += c[pos];
pos -= lowbit(pos);
}
return s;
}
int ans[maxn];
int main() {
int i;
while(scanf("%d", &n) != EOF) {
for(i = 1; i <= n; i++)
c[i] = 0;
for(i = 1; i <= n; i++) {
scanf("%d %d", &pt[i].A, &pt[i].B);
pt[i].A ++;
}
for(i = n; i >= 1; i--) {
int l = 1;
int r = n;
int as = 1;
while(l <= r) {
int m = (l + r) >> 1;
if(m - sum(m) >= pt[i].A) {
r = m - 1;
as = m;
}else
l = m + 1;
}
ans[as] = pt[i].B;
add(as);
}
for(i = 1; i <= n; i++) {
if(i != 1)
printf(" ");
printf("%d", ans[i]);
}
puts("");
}
return 0;
}