这个题本来我是想练堆用的,结果堆没练出来,倒是练了练线段树。
此题属于那种看起来特别简单,一做起来做出翔的那种;交了六遍,对拍了N年才A。
主要需要注意的是这么几点:
1、节点保存什么信息?
题目中让求的是距离最大,但这玩意儿在线段树中不太好办;
我一开始想当然地写成了r-l式的距离,结果合并信息的时候呵呵了。
更好的做法是保存区间中连续一段空橱子的个数:①设置变量意义时一定要从易于理解和调试的角度出发。
显然,最长连续区间的左右端点也是需要保存的,因为我们需要用它来求出球该放在什么地方。但是!若一个区间的长度为0,它的左右端点应该是哪里?
2、②若[l,r]的长度为0,则令l=r+1,r=l-1.
为什么?看看在合并的时候我们做了什么?
设struct TS{int lmax,lR,rmax,rL,max,L,R}
if(tree[node<<1].lR=((l+r)>>1)+1){
tree[node].lmax+=tree[node<<1|1].lmax;
tree[node].lR=tree[node<<1|1].lR;
}
这时,若tree[node<<1|1].lmax=0会发生什么呢?
若设此时tree[node<<1|1].lR=(l+r)>>1的话,则
tree[node<<1].lR=(l+r)>>1.
是正确的。
所以。。就这样吧。
3、③三个变量中选择最大的哪个该怎么做?
很容易混乱。。
条理清晰?
把条件都完整的列出来!
if(a>=b&&a>=c){}
if(b>=a&&b>=c){}
if(c>=a&&c>=b){}
4、球该放在哪?
显然球应该放在最长的连续无球橱子区间中,但是。。具体在哪里呢?
设区间[L,R]为一个最长连续无球橱子区间,且L>1,R<N.
则显然④球应该放在L-1与R+1的中位数的位置,才能使球与L-1和R+1的距离的最小值最大。
即x=(L-1+R+1)>>1=(L+R)>>1.
5、⑤特殊区间?
从1往右延伸的区间与从N往左延伸的区间,球在这里的距离长度就是区间长度!
6、记录的max?
这时我们发现一个很大很大的问题!就是节点中记录max,它不是那个最短距离,最短距离应该是max+1>>1!
也就是说,若果我们按max的大小比较,永远在max最大的前提下选择左边的那个,很可能会导致只比当前选出的max小1而其+1>>1与当前选出的max相同且在其右边的max被漏掉导致全盘皆输!
但是,如果我们不按max来选,是否可能导致当前选出的max比较小,使得之后合成的max比实际选出的max要小呢?也就是说一个max差了1不算,但两个差了2就不一样了啊!
但是我们突然发现了一个问题!max不会被合成,唯一与max有关的运算只是Cmp;合成是交给lmax和rmax的,而lmax和rmax又不需要Cmp!
于是。。在无数遍的WA与摸爬滚打之中,我们终于找到了正解的道路。。
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
struct TS{
int lmax,rmax,max,L,R,lR,rL;
}tree[800000];
int point[1000001];
#define root 1,1,N
#define lson node<<1,l,(l+r)>>1
#define rson node<<1|1,((l+r)>>1)+1,r
inline void update(int node,int l,int r,int x,int A){
if(l==r)
if(A)tree[node]=(TS){1,1,1,x-1,x+1,x+1,x-1};
else tree[node]=(TS){0,0,0,r,l,l,r};
else{
int m=(l+r)>>1;
if(x>m)update(rson,x,A);
else update(lson,x,A);
tree[node].lmax=tree[node<<1].lmax;
if(tree[node<<1].lR==m+1){
tree[node].lmax+=tree[node<<1|1].lmax;
tree[node].lR=tree[node<<1|1].lR;
}
else tree[node].lR=tree[node<<1].lR;
tree[node].rmax=tree[node<<1|1].rmax;
if(tree[node<<1|1].rL==m){
tree[node].rmax+=tree[node<<1].rmax;
tree[node].rL=tree[node<<1].rL;
}
else tree[node].rL=tree[node<<1|1].rL;
int tmp=tree[node<<1].rmax+tree[node<<1|1].lmax;
if(tree[node<<1].max+1>>1>=tree[node<<1|1].max+1>>1&&tree[node<<1].max+1>>1>=tmp+1>>1){
tree[node].max=tree[node<<1].max;
tree[node].L=tree[node<<1].L;
tree[node].R=tree[node<<1].R;
}
else
if(tmp+1>>1>=tree[node<<1].max+1>>1&&tmp+1>>1>=tree[node<<1|1].max+1>>1){
tree[node].max=tmp;
tree[node].L=tree[node<<1].rL;
tree[node].R=tree[node<<1|1].lR;
}
else{
tree[node].max=tree[node<<1|1].max;
tree[node].L=tree[node<<1|1].L;
tree[node].R=tree[node<<1|1].R;
}
}
//cout<<l<<","<<r<<":"<<tree[node].max<<"("<<tree[node].L<<"->"<<tree[node].R<<")"<<" "<<tree[node].lmax<<"("<<l<<"->"<<tree[node].lR<<")"<<" "<<tree[node].rmax<<"("<<tree[node].rL<<"<-"<<r<<")\n";
}
inline void build(int node,int l,int r){
tree[node]=(TS){r-l+1,r-l+1,r-l+1,l-1,r+1,r+1,l-1};
if(l!=r)build(lson),build(rson);
}
inline void out(int node,int l,int r){
if(l!=r)out(lson),out(rson);
else cout<<!tree[node].max;
}
int main(){
freopen("CODEVS3032.in","r",stdin);
//freopen("CODEVS3032.out","w",stdout);
memset(tree,0,sizeof(tree));
int N,M,flag,A,x,tot=0,l,m,r;
scanf("%d%d",&N,&M);
build(root);
while(M--){
scanf("%d%d",&flag,&A);
if(flag-1)update(root,point[A],1);
else{
//cout<<tree[1].lmax<<" "<<(tree[1].max+1>>1)<<" "<<tree[1].rmax<<endl;
if(tree[1].lmax>=tree[1].rmax&&tree[1].lmax>=tree[1].max+1>>1)x=1;
else
if(tree[1].max+1>>1>=tree[1].rmax&&tree[1].max+1>>1>=tree[1].lmax)x=(tree[1].L+tree[1].R)>>1;
else x=N;
printf("%d\n",x);
update(root,x,0);
point[A]=x;
}/*
cout<<flag<<" "<<A<<":";
out(root);
cout<<endl;*/
}
}
⑦重要总结:不论是多么简单的题,一定要写对拍!这个题中一半的问题全是拍出来的!有很多题都是看似简单实则不易。