Codeforces#135(div.2) E.Parking lot【线段树】

E. Parking Lot
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

A parking lot in the City consists of n parking spaces, standing in a line. The parking spaces are numbered from 1 to n from left to right.

When a car arrives at the lot, the operator determines an empty parking space for it. For the safety's sake the chosen place should be located as far from the already occupied places as possible. That is, the closest occupied parking space must be as far away as possible. If there are several such places, then the operator chooses the place with the minimum index from them. If all parking lot places are empty, then the car gets place number 1.

We consider the distance between the i-th and the j-th parking spaces equal to 4·|i - j| meters.

You are given the parking lot records of arriving and departing cars in the chronological order. For each record of an arriving car print the number of the parking lot that was given to this car.

Input

The first line contains two space-separated integers n and m (1 ≤ n, m ≤ 2·105) — the number of parking places and the number of records correspondingly.

Next m lines contain the descriptions of the records, one per line. The i-th line contains numbers tiidi (1 ≤ ti ≤ 2; 1 ≤ idi ≤ 106). If tiequals 1, then the corresponding record says that the car number idi arrived at the parking lot. If ti equals 2, then the corresponding record says that the car number idi departed from the parking lot.

Records about arriving to the parking lot and departing from the parking lot are given chronologically. All events occurred consecutively, no two events occurred simultaneously.

It is guaranteed that all entries are correct:

  • each car arrived at the parking lot at most once and departed from the parking lot at most once,
  • there is no record of a departing car if it didn't arrive at the parking lot earlier,
  • there are no more than n cars on the parking lot at any moment.

You can consider the cars arbitrarily numbered from 1 to 106, all numbers are distinct. Initially all places in the parking lot are empty.

Output

For each entry of an arriving car print the number of its parking space. Print the numbers of the spaces in the order, in which the cars arrive to the parking lot.

Sample test(s)
input
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
output
1
7
4
2
7
4
1
3

题意:停车场有n(1<=n<=2*10^5)个停车位,从左到右依次编号为1到n。有两种共m(1<=m<=2*10^5)次操作:“1 id”表示编号为id的车驶入停车场并停车;“2 id”表示编号为id的车驶出停车场。停车时有一个要求,就是将车停在离其它车尽量远的位置,如果有多个位置可选,选择最左边的那个。

分析:根据题目要求及数据范围,很快就能确定要用线段树来解决。本题细节比较多,WA了很多次。

首先,我开始时想当然的认为每次停车时,找到[1, n]区间内最长且最左边的连续的空闲区间(设起始位置为start,结束位置为end),那么停车的位置就是(start - end + 2) / 2(start为1或end为n时要特殊处理),但这样却过不了样例。于是,我模拟了一下样例的停车过程,发现了问题:

设n为7,那么当停车情况为“0 1 0 1 0 0 1”时(0表示没车,1表示有车),下一辆车应该停在第1个车位,而我得到的结果是将车停在第5个车位。

所以,只维护区间内最长的连续空闲区间是不够的,因为车不一定停在最长的那个连续空闲区间内。好了,就说这么多,附上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;

const int maxn = 200010;
int n, m;
/*
llen: 区间左端点开始连续的空位的长度 
rlen: 区间右端点开始连续的空位的长度 
mlen: 整个区间最长的连续的空位的长度 
*/
int llen[maxn<<2], mlen[maxn<<2], rlen[maxn<<2];
/*
start: 如果将车停在该区间,停车的位置所在的连续空位的起始位置
end: 如果将车停在该区间,停车的位置所在的连续空位的结束位置
ms: 该区间最长的连续的空位的起始位置 
*/
int start[maxn<<2], end[maxn<<2], ms[maxn<<2];
int pos[maxn*5];

void pushUp(int l, int r, int rt)
{
    int m = (l + r) >> 1;
    llen[rt] = llen[rt<<1];
    rlen[rt] = rlen[rt<<1|1];
    if (llen[rt] == m - l + 1) {
        llen[rt] += llen[rt<<1|1];
    }
    if (rlen[rt] == r - m) {
        rlen[rt] += rlen[rt<<1];
    }
    /*求区间最长的连续空位的长度及起始位置*/
    if (mlen[rt<<1] >= (rlen[rt<<1] + llen[rt<<1|1]) && mlen[rt<<1] >= mlen[rt<<1|1]) {
        mlen[rt] = mlen[rt<<1];
        ms[rt] = ms[rt<<1];
    } else if (rlen[rt<<1] + llen[rt<<1|1] >= mlen[rt<<1|1]) {
        mlen[rt] = rlen[rt<<1] + llen[rt<<1|1];
        ms[rt] = m - rlen[rt<<1] + 1;
    } else {
        mlen[rt] = mlen[rt<<1|1];
        ms[rt] = ms[rt<<1|1];
    }
    /*求如果将车停在该区间,停车的位置所在的连续空位的起始及结束的位置*/
    int dis, dis1, dis2;
    dis1 = (mlen[rt<<1] + 1) / 2;
    dis2 = (mlen[rt<<1|1] + 1) / 2;
    dis = (rlen[rt<<1] + llen[rt<<1|1] + 1) / 2;
    if (dis1 >= dis && dis1 >= dis2) {
        start[rt] = start[rt<<1];
        end[rt] = end[rt<<1];
    } else if (dis >= dis2) {
        start[rt] = m - rlen[rt<<1] + 1;
        end[rt] = m + llen[rt<<1|1];
    } else {
        start[rt] = start[rt<<1|1];
        end[rt] = end[rt<<1|1];
    }
    return ;
}

void build(int l, int r, int rt)
{
    if (l == r) {
        llen[rt] = mlen[rt] = rlen[rt] = 1;
        ms[rt] = start[rt] = end[rt] = l;
        return ;
    }
    int m = (l + r) >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    pushUp(l, r, rt);
}

void update(int l, int r, int rt, int p, int c)
{
    if (l == r) {
        llen[rt] = mlen[rt] = rlen[rt] = c;
        ms[rt] = start[rt] = end[rt] = l;
        return ;
    }
    int m = (l + r) >> 1;
    if (p <= m) {
        update(l, m, rt << 1, p, c);
    } else {
        update(m + 1, r, rt << 1 | 1, p, c);
    }
    pushUp(l, r, rt);
}

int main()
{
    //freopen("parking.in", "r", stdin);
    //freopen("parking.out", "w", stdout);
    scanf("%d%d", &n, &m);
    build(1, n, 1);
    int t, id;
    int from, to, len;
    for (int i = 0; i < m; ++i) {
        scanf("%d%d", &t, &id);
        if (t == 1) {
            from = start[1];
            to = end[1];
            len = to - from + 1;
            // 这里一开始只写了llen[1] >= (len + 1) / 2,WA一次 
            if (llen[1] >= (len + 1) / 2 && llen[1] >= rlen[1]) {
                update(1, n, 1, 1, 0);
                pos[id] = 1;
            } else if (rlen[1] > (len + 1) / 2) {
                update(1, n, 1, n, 0);
                pos[id] = n;
            } else {
                update(1, n, 1, (from + to) / 2, 0);
                pos[id] = (from + to) / 2;
            }
            printf("%d\n", pos[id]);
        } else {
            update(1, n, 1, pos[id], 1);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值