阿Q的停车场
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿Q的停车场相关的知识,希望对你有一定的参考价值。
刚拿到驾照的 KJ 总喜欢开着车到处兜风,玩完了再把车停到阿 Q 的停车场里,虽然 她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是 Kelukin。于是, 她每次都把自己的爱车停在距离其它车最远的一个车位。KJ 觉得自己这样的策略非常科 学,于是她开始想:在一个停车场中有一排车位,从左到右编号为 1 到 n,初始时全部是 空的。有若干汽车,进出停车场共 m 次。对于每辆进入停车场的汽车,会选择与其它车距 离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ 想着想着就睡着了, 在她一旁的 Kelukin 想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把 这个问题就留给了你:在 KJ 理想的阿 Q 的停车场中,给你车辆进出的操作序列,依次输 出每辆车的车位编号。
【输入说明】 第一行,两个整数 n 和 m,表示停车场大小和操作数; 接下来 m 行,每行两个整数 F 和 x F 是 1 表示编号为 x 的车进停车场; F 是 2 表示编号为 x 的车出停车场; 保证操作合法,即: 出停车场的车一定目前仍在停车场里; 停车场内的车不会超过 n;
【输出说明】 对于所有操作 1,输出一个整数,表示该车车位的编号。
【样例输入输出】
park.in park.out
7 11 1
1 15 7
1 123123 4
1 3 2
1 5 7
2 123123 4
2 15 1
1 21 3
2 3
1 6
1 7
1 8
题目大意:考虑“会选择与其它车距 离最小值最大的一个车位”,如果我们将0和n+1看成一样停了车车的话,每次寻找位置前后可以停车的位置都是很多段连续的区间,因为左右都有车(区间内)那么最近的话应该是中间位置,所以停在区间的最大就是中间位置,考虑所有的可停位置,我们机智地选取最长的区间的中间位置,然后修改。
区间问题,自然想到线段树(堆应该也可以维护)
线段树的题目我一般分两个步骤考虑,也就是线段树的两个基本操作来找到应该维护的东西和具体特殊操作:
Query:目标是找到数列里面最长的子段,可以考虑维护每个节点的区间内的最长,但是会有跨越两个区间的情况,比如1-4,23位可停,那么单纯的维护就会得到1,事实上,左段的右端与右段的左端构成了一个更大的子段,因此考虑用:mlen( 总的最大len )llen(从左端开始的len),rlen(从右端开始的len)分别表示
那么左儿子的rlen加右儿子的llen就可以处理这种情况,基本的定义找到后,查询就很简单了:一直往下找,如果我们最大的结果(即1-m的mlen)在左边,往左找,如果在中间可以利用mid=(l+r)/2和ls.rlen与rs.len求出位置,再顺便更新,两个都不成立再往右,因为我们在同样优的情况下要找到最左边的停车位;
Update:接下来考虑能否修改,修改一个最底层的结点我们可以单纯的吧这个点的llen,mlen,rlen都改变,但是向上传递时mlen可以用上端说的处理,llen和rlen就不行。以llen举例,在左右合并时,左儿子的llen肯定是可以用的,但是如果它被扩大,那么一定是和右儿子的左端连起来了,那么连起来的条件是这个区间全可以停车,也解决了。
今天我的编译器出了点问题,代码可能有点错,最好不要对着代码打;
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #define N 200100 10 #define M 1000001 11 #define Run(i,l,r) for(int i=l;i<=r;i++) 12 #define Don(i,l,r) for(int i=l;i>=r;i--) 13 using namespace std; 14 int n,m; 15 int num[M]; 16 struct node{ 17 int left,right,mlen,llen,rlen; 18 }tree[4*N]; 19 void Build(int e,int l,int r) 20 { tree[e].left=l; tree[e].right=r; 21 if (l==r) tree[e].mlen=tree[e].llen=tree[e].rlen=1; 22 else {int mid=(l+r)/2; 23 Build(2*e,l,mid); 24 Build(2*e+1,mid+1,r); 25 tree[e].mlen=tree[e].rlen=tree[e].llen=tree[2*e].mlen+tree[2*e+1].mlen; 26 } //建树 27 } 28 void Update(int e,int l,int r,int pos) 29 { if (tree[e].left==tree[e].right) { 30 tree[e].mlen=tree[e].llen=tree[e].rlen=tree[e].mlen^1; 31 } 32 else {int mid=(l+r)/2; int len1=mid-l+1,len2=r-mid; 33 if (pos<=mid) Update(2*e,l,mid,pos); 34 else Update(2*e+1,mid+1,r,pos); 35 tree[e].mlen=max(tree[2*e].rlen+tree[2*e+1].llen,max(tree[2*e].mlen,tree[2*e+1].mlen)); 36 tree[e].llen=(tree[2*e].llen==len1)?(len1+tree[2*e+1].llen):(tree[2*e].llen); 37 tree[e].rlen=(tree[2*e+1].rlen==len2)?(len2+tree[2*e].rlen):(tree[2*e+1].rlen); 38 } 39 } 40 int Query(int e,int l,int r) 41 { int mid=(l+r)/2; 42 if (tree[e].mlen==tree[2*e].mlen) { 43 return Query(2*e,l,mid); 44 } 45 else if (tree[e].mlen==tree[2*e].rlen+tree[2*e+1].llen){ 46 int pos=(mid-tree[2*e].rlen+1+mid+tree[2*e+1].llen)/2; 47 return pos; 48 Update(1,1,n,pos); 49 } //计算中间位置 50 else { 51 return Query(2*e+1,mid+1,r); 52 } 53 } //注意顺序 54 55 int mian() 56 { 57 freopen("park.in","r",stdin); 58 freopen("park.out","w",stdout); 59 cin>>n>>m; 60 int ques,x; 61 Build(1,1,n); 62 Run(i,1,m) 63 {cin>>ques>>x; 64 if (ques==1) {num[x]=Query(1,1,n); cout<<num[x]<<endl;} 65 else if (ques==2) {Update(1,1,n,num[x]);} 66 } 67 return 0; 68 }
以上是关于阿Q的停车场的主要内容,如果未能解决你的问题,请参考以下文章