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;
}

  

posted @ 2015-10-29 11:50  xing-xing  阅读(94)  评论(0)    收藏  举报