题目描述
刚拿到驾照的KJ 总喜欢开着车到处兜风,玩完了再把车停到阿Q的停车场里,虽然她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是Kelukin。于是,她每次都把自己的爱车停在距离其它车最远的一个车位。KJ 觉得自己这样的策略非常科学,于是她开始想:在一个停车场中有一排车位,从左到右编号为 1 到 n,初始时全部是空的。有若干汽车,进出停车场共 m 次。对于每辆进入停车场的汽车,会选择与其它车距离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ 想着想着就睡着了,在她一旁的Kelukin想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把这个问题就留给了你:在KJ 理想的阿 Q 的停车场中,给你车辆进出的操作序列,依次输出每辆车的车位编号。
输入格式
第一行,两个整数 n 和 m,表示停车场大小和操作数;
接下来 m 行,每行两个整数,F 和 x
F 是 1 表示编号为 x 的车进停车场;
F 是 2 表示编号为 x 的车出停车场;
保证操作合法,即:
出停车场的车一定目前仍在停车场里;
停车场内的车不会超过 n;
输出格式
对于所有操作 1,输出一个整数,表示该车车位的编号。
样例输入
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
样例输出
1
7
4
2
7
4
1
3
数据范围
对30%的数据 n<=1000 ,m<=1000
对60%的数据 n<=200000,m<=2000
对100%的数据n,m<=200000,车的编号小于等于 10^6
思路
30%
第一边做打了一个爆搜,维护停车场和距离最大值两个数组,进出车辆各自维护,进车的时候两个指针分别指向左右端点,维护值即为原值和更新值中较小的;出车的时候因为不知道此时的值是谁赋给它的,所有把空车位全部填满重来一遍。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
const ma=200000; var f:array[1..ma] of longint; a:array[1..ma] of longint; n,m,k,x,y,num,ss,kk:longint; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; procedure intt; begin assign(input,'park.in'); assign(output,'park.out'); reset(input); rewrite(output); end; procedure outt; begin close(input); close(output); end; function update1(x:longint):longint; var i,j,k,sum,num:longint; begin i:=x;j:=x; while i>=1 do begin if a[i]=0 then f[i]:=min(f[i],abs(x-i)); dec(i); end; while j<=n do begin if a[j]=0 then f[j]:=min(f[j],abs(x-j)); inc(j); end; num:=0;sum:=0; for i:=1 to n do if (a[i]=0)and(f[i]>sum) then begin sum:=f[i]; num:=i; end; exit(num); end; //car in function update2(x:longint):longint; var i,j,num,sum:longint; begin for i:=1 to n do if a[i]=0 then f[i]:=2139062143; for i:=1 to n do if a[i]<>0 then for j:=1 to n do if a[j]=0 then f[j]:=min(f[j],abs(i-j)); num:=0;sum:=0; for i:=1 to n do if (a[i]=0)and(f[i]>sum) then begin sum:=f[i]; num:=i; end; exit(num); end; //car out begin intt; fillchar(f,sizeof(f),127); fillchar(a,sizeof(a),0); readln(n,m); for k:=1 to m do begin readln(x,y); if k=1 then begin a[1]:=y; writeln(1); ss:=update1(1); continue; end; if x=1 then begin writeln(ss); a[ss]:=y; f[ss]:=2139062143; ss:=update1(ss); continue; end; if x=2 then begin for kk:=1 to n do if a[kk]=y then begin a[kk]:=0; f[kk]:=2139062143; ss:=update2(kk); break; end; continue; end; end; outt; end.
45%
XYD维护了某车两侧的空车位,我也不知道是怎么维护的,反正也是爆搜。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
program dddlk; var a,l,r:array[0..200001]of longint; f:array[1..200000]of boolean; b:array[1..1000000]of longint; n,m,i,j,a1,a2,ans,b1,max,b2:longint; begin assign(input,'park.in'); assign(output,'park.out'); reset(input); rewrite(output); ans:=1; read(n,m); a[0]:=-300000; a[n+1]:=-300000; l[0]:=-300000; r[n+1]:=500000; for i:=1 to n do a[i]:=maxlongint; for i:=1 to m do begin read(a1,a2); if a1=1 then begin writeln(ans); b[a2]:=ans; f[ans]:=true; a[ans]:=0; r[ans]:=ans; l[ans]:=ans; b1:=ans-1; while(b1>0)and(not f[b1])do begin if a[b1]>ans-b1 then a[b1]:=ans-b1; r[b1]:=ans; dec(b1); end; b1:=ans+1; while(b1<n+1)and(not f[b1])do begin if a[b1]>b1-ans then a[b1]:=b1-ans; l[b1]:=ans; inc(b1); end; max:=a[1]; ans:=1; for j:=2 to n do if a[j]>max then begin max:=a[j]; ans:=j; end; end else begin b2:=b[a2]; b1:=b2-1; f[b2]:=false; while(b1>0)and(not f[b1])do begin r[b1]:=r[b2+1]; if b1-l[b1]>r[b1]-b1 then a[b1]:=r[b1]-b1 else a[b1]:=b1-l[b1]; dec(b1); end; b1:=b2+1; while(b1<n+1)and(not f[b1])do begin l[b1]:=l[b2-1]; if b1-l[b1]>r[b1]-b1 then a[b1]:=r[b1]-b1 else a[b1]:=b1-l[b1]; inc(b1); end; l[b2]:=l[b2-1]; r[b2]:=r[b2+1]; if b2-l[b2]>r[b2]-b2 then a[b2]:=r[b2]-b2 else a[b2]:=b2-l[b2]; max:=a[1]; for j:=2 to n do if a[j]>max then begin max:=a[j]; ans:=j; end; end; end; close(input); close(output); end.
60%
不晓得这个怎么搞
100%
用线段树实现。(据某位大神说,可以用堆来做。然而我并不会)用线段树还是相对简单的。首先我们对区间[1..n]开一课线段树。对于每一个节点,维护4个值。分别是l,r,mid,p。l表示在当前结点线段树所在区间,最左边的车停的位置。同理,r表示做右边的车所停的位置。mid表示在这个小区间[l,r]中的紧邻的两辆车的最长距离除以2后的值。p表示取得mid值是所在的紧邻的两辆车的中间位置,也就是在[l,r]中的答案值。
对于 1 询问:访问线段树的第一个节点,我们比较l-1,n-r,mid的值哪个更大,就选哪个,它们的答案依次是1,n,mid。假设我们求得的位置是car[x]。然后访问[car[x],car[x]]所在的线段树的叶子节点,初始化它的值,然后回溯,进行合并。对于h[x].l与h[x].r可以通过两个儿子的l,r信息得出。对于h[x].mid值,首先在左右儿子的mid值中去一个最大的值。其次考虑一种情况,就是夹在两个线段之间的距离,可以通过(h[x+x+1].l-h[x+x].r) div 2 的值得出在于mid进行比较,然后p就随着mid的值的更新而更新。
对于2询问:访问询问车所在的位置,直接将它的叶子节点[car[x],car[x]]删除,然后回溯时,再做一次合并操作。即可
AC做法:http://blog.csdn.net/liuyuanzhe0515/article/details/47414221
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
const maxc=1000005;maxn=200005; type node=record l,r,mid,p:longint; end; var i,j,k,m,n,ch,num,sum:longint; car:array[1..maxc] of longint; h:array[1..4*maxn] of node; procedure merger(x:longint); var t:longint; begin if h[x+x].l>0 then h[x].l:=h[x+x].l else h[x].l:=h[x+x+1].l; if h[x+x+1].r>0 then h[x].r:=h[x+x+1].r else h[x].r:=h[x+x].r; h[x].mid:=h[x+x].mid; h[x].p:=h[x+x].p; if (h[x+x+1].l>0) and (h[x+x].r>0) then begin t:=(h[x+x+1].l-h[x+x].r) div 2; if t>h[x].mid then begin h[x].mid:=t; h[x].p:=(h[x+x+1].l+h[x+x].r) div 2; end; if h[x+x+1].mid>h[x].mid then begin h[x].mid:=h[x+x+1].mid; h[x].p:=h[x+x+1].p; end; end; end; procedure work(x,l,r,num,kind:longint); var mid:longint; begin if l=r then begin if kind=2 then begin h[x].l:=0;h[x].r:=0; h[x].mid:=0;h[x].p:=0; end else begin h[x].l:=l;h[x].r:=r; h[x].mid:=0;h[x].p:=0; end; exit; end; mid:=(l+r)>>1; if num<=mid then work(x+x,l,mid,num,kind) else work(x+x+1,mid+1,r,num,kind); merger(x); end; begin readln(n,m); for i:=1 to m do begin readln(ch,num); if ch=1 then begin if h[1].l=0 then begin car[num]:=1; end else begin sum:=-maxlongint; if h[1].l-1>sum then begin sum:=h[1].l-1; car[num]:=1; end; if h[1].mid>sum then begin sum:=h[1].mid; car[num]:=h[1].p; end; if n-h[1].r>sum then begin sum:=n-h[1].r; car[num]:=n; end; end; writeln(car[num]); work(1,1,n,car[num],1); end else begin work(1,1,n,car[num],2); end; end; end.