【POJ】2828 Buy Tickets 题解

题目大意

n n n个人前来排队,第 i i i个人编号为 v a l [ i ] val[i] val[i],来了之后会站在队伍中第 p [ i ] p[i] p[i]个人后面,问最后的队伍是什么样子的。

题目链接

思路

我们注意到,每次有新来的人来插队,他只会影响到已经在队伍中的人的占位,而之前的人的占位不会影响后面的人的占位。

而对已经在队伍中的人做修改,时间复杂度较高,我们不妨反着考虑。从最后一个人开始,往前考虑。此时对于每个人的位置,我们只需要看有多少个人站在了它前面

这样我们就从修改多个人的位置,变成了维护后来的人对前面的人的影响。从’一对多’,变成了’多对一’。而我们有很多种算法来解决这种问题

那么第 n n n个人的占位就是 p [ i ] + 1 p[i] + 1 p[i]+1,接下来考虑第 p [ n − 1 ] p[n-1] p[n1],如果

  • p [ n ] > p [ n − 1 ] p[n] > p[n-1] p[n]>p[n1],即第 n n n站在了第 n − 1 n-1 n1个人的后面,那么 a n s [ n − 1 ] = p [ n − 1 ] + 1 ans[n-1] = p[n-1]+1 ans[n1]=p[n1]+1
  • p [ n ] < > p [ n − 1 ] p[n] <> p[n-1] p[n]<>p[n1],即第 n n n站在了第 n − 1 n-1 n1个人的前面,那么 a n s [ n − 1 ] = p [ n − 1 ] + 2 ans[n-1] = p[n-1]+2 ans[n1]=p[n1]+2

所以我们的任务就是计算第 i i i个人前面站了多少人,再加上 p [ i ] + 1 p[i] + 1 p[i]+1就是最终答案。

那么其实就是相当于寻找第 p [ i ] + 1 p[i] + 1 p[i]+1个空余的位置。

因为它的最终位置就是 p [ i ] + 1 + c n t p[i] + 1 + cnt p[i]+1+cnt(前面人的数量),那么就说明前面有 c n t cnt cnt个位置被占用了,那么减掉 c n t cnt cnt,就得出了它前面有 p [ i ] p[i] p[i]个空余位置

代码

#include <cstdio>
#include <iostream>
using namespace std;

const int maxN = 2e5 + 7;

int n, ans[maxN], a[maxN], val[maxN];

struct Tree{
    int l, r, sum;
}t[maxN << 2];

void build(int l, int r, int num)
{
    t[num].l = l; t[num].r = r;
    if(l == r) {
        t[num].sum = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, num << 1);
    build(mid + 1, r, num << 1 | 1);
    t[num].sum = t[num << 1].sum + t[num << 1 | 1].sum;
}

void change(int num, int pos, int x, int val) // 寻找第x个空余位置,并修改,然后记录答案
{
    if(t[num].l == t[num].r) {
        t[num].sum += x;
        ans[t[num].l] = val;
        return ;
    }
    int mid = (t[num].l + t[num].r) >> 1;
    if(pos <= t[num << 1].sum)
        change(num << 1, pos, x, val);
    else 
        change(num << 1 | 1, pos - t[num << 1].sum, x, val);
    t[num].sum = t[num << 1].sum + t[num << 1 | 1].sum;
}

int main()
{
    while(~scanf("%d", &n)) {
        build(1, n, 1);
        for(int i = 1; i <= n; ++i) 
            scanf("%d%d", &a[i], &val[i]);
        for(int i = n; i >= 1; --i)
            change(1, a[i] + 1, -1, val[i]);
        for(int i = 1; i <= n; ++i)
            printf("%d ", ans[i]);
        printf("\n");
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值