POJ2828 线段树 xingxing在努力
很好地一道线段树题,此题大概就是模拟生活中的插队问题, 比如第一组数据,
4 0 77 1 51 1 33 2 69
插入过程如下, 77 ->77 51 -> 77 33 51 -> 77 33 69 51.刚开始对于这个题还真是没一点思路, 看了下题解的思路,逆向思维, 最后一个人肯定能得到他的位置, 因此我们将所有的插入操作保存下来逆着插入数组就行。对于第一组数据插入2 69后排序数组就变为0 0 69 0, 然后给排序数据中0元素重新编号插入1 33得到0 33 69 0,依次类推得到77 33 69 51.观察上述的插入过程我们可以看到插入2 69时插入的位置前面有2个空位置, 插入33的时候插入为值得前面有1个空位置, 因此假设我们能快速的找到哪一个位置前面有n个空位置这样问题就迎刃而解了。我们想到用线段树维护一个区间内空位置的个数。代码如下
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 200000 + 10;
int num[maxn];
int a[maxn], b[maxn];
struct segment
{
int l, r;
int free; //线段树的结点记录该区间空位置的个数
}seg[3*maxn];
void build(int rt ,int l, int r)
{
seg[rt].l = l;
seg[rt].r = r;
seg[rt].free = (r-l+1);
if(l == r) return ;
int chl = 2*rt, chr = 2*rt + 1;
build(chl, l, (l+r)/2);
build(chr, (l+r)/2+1, r);
}
int query(int rt, int i)
{
if(seg[rt].l==seg[rt].r)
{
seg[rt].free--;
return seg[rt].l;
}
int v;
if(i >= seg[2*rt].free)
v = query(2*rt+1, i-seg[2*rt].free);
else
v = query(2*rt, i);
int chl = 2*rt, chr = 2*rt+1;
seg[rt].free = seg[chl].free + seg[chr].free;
return v;
}
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
build(1, 1, n);
for(int i=0; i<n; i++)
scanf("%d%d", &a[i], &b[i]);
for(int i=n-1; i>=0; i--)
{
int idx = query(1, a[i]);
num[idx] = b[i];
}
for(int i=1; i<n; i++)
printf("%d ", num[i]);
printf("%d\n", num[n]);
}
return 0;
}

浙公网安备 33010602011771号