【poj】2828 Buy Tickets、2182 Lost Cow(线段树-单点更新)

题目大意:人们一个一个的来排队并插队,按人到来的顺序给出每个人插队的位置(插在第几个人后面),并告知每个人的id号,输出最终队伍的情况。
思路:这道题假如你正向去处理的话,会发现,当你确定一个位置的时候,会面临之后还需要将这个位置以及这个位置后面的所有位置下调一个位置,这样在插入上,会有很大的复杂度。
所以遇到这种情况不如从后面开始向前遍历。而且这个时候他所要插入的位置便是他最终的位置,假如这个位置没有人的话,便不用动了。
而且将位置看作前面所需要留空的位置,便好处理很多(十分微妙,需要好好体会!)
sum[rt]表示区间空的位置
AC代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 200002
int sum[MAX << 2];
int ans[MAX];
int id[MAX];
int s[MAX];

void init(int l,int r,int rt)
{
    if (l == r)
        sum[rt] = 1;
    else
    {
        int mid = (l + r) >> 1;
        init(l, mid, rt << 1);
        init(mid + 1, r, rt << 1 | 1);
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
}

int update(int pos, int l, int r, int rt)
{
    sum[rt]--;
    if (l == r)
        return l;
    int mid = (r + l) >> 1;
    if (pos <= sum[rt << 1])
        return update(pos, l, mid, rt << 1);
    else
        return update(pos - sum[rt << 1], mid + 1, r, rt << 1 | 1);
}

int main()
{
    int n;
    while (cin >> n)
    {
        init(1, n+1, 1);
        for (int i = 0; i < n; i++)
            scanf("%d%d", &id[i], &s[i]);
        for (int i = n - 1; i >= 0; i--)
        {
            ans[update(id[i]+1, 1, n+1, 1)-1] = s[i];
        }
        printf("%d", ans[0]);
        for (int i = 1; i <n; i++)
            printf(" %d", ans[i]);
        cout << endl;
    }
}

POJ 2182
给出的数字表示从第2到第n只牛左边有几个比它序号小的牛,要求每个位置的牛的编号是多少
思路与上面的一样,只有最后一个你才能够真正的确认编号

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 8008
#define ls rt<<1
#define rs rt<<1|1
#define m (l+r)>>1
int sum[MAX << 2];
int a[MAX];
int ans[MAX];
bool vis[MAX];

void uprt(int rt)
{
    sum[rt] = sum[ls] + sum[rs];
}

void build(int l, int r, int rt)
{
    if (l == r)
    {
        sum[rt] = 1;
        return;
    }
    int mid = m;
    build(l, mid, ls);
    build(mid + 1, r, rs);
    uprt(rt);
}

int query(int p, int l, int r, int rt)
{
    sum[rt]--;
    if (l == r)
        return l;
    int mid = m;
    int ans = 0;
    if (p < sum[ls])
        ans = query(p, l, mid, ls);
    else
        ans = query(p - sum[ls], mid + 1, r, rs);
}

int main()
{
    int n;
    while (cin >> n)
    {
        build(1, n, 1);
        memset(vis, 0, sizeof(vis));
        for (int i = 0; i < n-1; i++)
            scanf("%d", &a[i]);
        for (int i = n - 2; i >= 0;i--)
        {
            ans[i] = query(a[i], 1, n, 1);
            vis[ans[i]] = 1;
        }
        for (int i = 1; i <= n;i++)
        if (!vis[i])
            cout << i << endl;
        for (int i = 0; i < n - 1; i++)
            cout << ans[i] << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值