题意:停车场有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;
}