POJ 2828 Buy Tickets 题解&代码

题目大意:对于每组数据,给出一个空队列,有m次插入,每次插入都将在队列的第pos[i]处插入一个值val[i],输出队列最后的状态。

思路:
在第pos[i]处插入一个元素,则第pos[i]处后的所有元素都将向后移动一位,看起来是平均每次插入需要n次操作。
但是我们可以看出,对每次插入来说,本次插入结束后本次所插入的元素位置一定会是pos[i]+1,那么与其先插入可能会需要向后移动的元素,不如从最后一个被插入的元素开始向一个[0,m]的空区间插入元素,这样每一次插入都不会对已经记录的状态产生影响。
这样的话,可以将这道题转换成线段树来做,用一个s标记统计区间内被插入的元素数,用v数组记录区间上的单点被插入值。
第i个元素的位置应当是从0开始的空位置中的第pos[i]个,我们可以用(r-l+1)-s[o]来统计区间编号为o的区间内有多少个空位置,如果(r-l+1)-s[o]>pos[i],那么这个区间包含该元素应该被插入的区间。

对代码能力要求不高,注意判定的细节和线段树的特性,基本上过样例就说明AC…我这种手残大约40min就调过了

**代码中的边界判定与思路中所说有微小区别

#include<iostream>
#include<stdio.h>
#define lson (o<<1)
#define rson ((o<<1)|1)
using namespace std;
const int maxn=200005;
bool flag,f;
int n,p[maxn],val[maxn],v[maxn],s[maxn*4];
void build_tree(int o,int l,int r)
{
    s[o]=0;
    if(l==r)
    {
        v[l]=0;
        return;
    }
    int mid=(l+r)/2;
    build_tree(lson,l,mid);
    build_tree(rson,mid+1,r);
}
void maintain(int o,int l,int r)
{
    s[o]=s[lson]+s[rson];
}
void add_tree(int o,int l,int r,int z,int x)
{
    if(l==r)
    {
        if(s[o])return;
        v[l]=x;
        s[o]=1;
        f=true;
        return;
    }
    int mid=(l+r)/2;
    if(mid>=z && z<=(mid-l+1)-s[lson])add_tree(lson,l,mid,z,x);
    else
    {
        z-=(mid-l+1)-s[lson];
        add_tree(rson,mid+1,r,z,x);
    }
    maintain(o,l,r);
}
void print_tree(int o,int l,int r)
{
    if(l==r)
    {
        if(flag)printf(" %d",v[l]-1);
        else printf("%d",v[l]-1);
        flag=true;
        return;
    }
    int mid=(l+r)/2;
    print_tree(lson,l,mid);
    print_tree(rson,mid+1,r);
}
int main(void)
{
    while(cin>>n)
    {
        flag=false;
        build_tree(1,1,n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i],&val[i]);
            p[i]++;val[i]++;
        }
        for(int i=n;i>0;i--)
        {
            f=false;
            add_tree(1,1,n,p[i],val[i]);
        }
        print_tree(1,1,n);
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(POJ 2828 Buy Tickets 题解&代码)