首页 > 代码库 > [CODEVS3032]摆放球 解题报告
[CODEVS3032]摆放球 解题报告
这个题本来我是想练堆用的,结果堆没练出来,倒是练了练线段树。
此题属于那种看起来特别简单,一做起来做出翔的那种;交了六遍,对拍了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;*/ } }
⑦重要总结:不论是多么简单的题,一定要写对拍!这个题中一半的问题全是拍出来的!有很多题都是看似简单实则不易。
[CODEVS3032]摆放球 解题报告