BZOJ 2732: [HNOI2012]射箭 题解
来源:互联网 发布:php 判断是否含有空格 编辑:程序博客网 时间:2024/04/27 14:54
传送门如下:
BZOJ 2732
洛谷 3222
数据分析
这两个OJ网站各有特点,其中BZOJ对一些特殊情况有预判(最重要的是抛物线的位置有限制),洛谷则对时间卡的很严,所以两个都有必要AC一下(我是洛谷第5个AC这题的,在这撒个花吧,heheda)
题目里说的很清楚了。
解题分析
这道题给的初始条件就是一条条线段,那么先试试转化一下
已知x0,y1,y2是常量,a和b是自变量,有没有发现,右边的式子可以看成平面上的一条直线呢?那再结合左边,就是判断是直线的上边还是下边,还有,直线把一个平面分成了两部分,每一部分就是一个——半平面,好吗,那就想到半平面交了。把每一关转换成两半平面,如果能穿过这K个靶子,那就说明这2K个半平面的交非空。然后核心思想就完了。
下面是温馨提示
1
有没有发现,如果加入的半平面越多,那么半平面交只会变小不会变大,那么这样一个递减的状态,可以考虑二分优化,而不是每次加入求一次,然后再加入再求一次。
2
求半平面交的时候要先对极角排序,但是每次二分拍一次很烦,所以可以一开始先对所有半平面标号,然后对极角排序,求半平面交的时候先扫一趟把符合范围内的加进去,然后再求半平面交。
3
还有一个莫名其妙的优化,根据前面写的推导公式(以上面的为例),可以得到对应有向直线点为(0,y1/x0),方向向量为(1,-x0),然而把方向向量改成(1/x0,-1),然后时间就优下来了。
4
洛谷的时间问题解决了,然后就是BZOJ,首先求半平面交的时候会给四边加个框,但是注意抛物线y=a* x^2+b*x中根据题意必须a<0,b>0,所以框要定在第二象限,还有,由于不带等号,所以不能为0,可以是一个很小的常数1e-15
5
所求的半平面交有可能是一个点或一条线段,这种时候队列中也til-hed<=1,这不是加个等号就能解决的,需要将靶子两端分别延长。这样点和线段就成了多边形,然后就解决了。
时间:O(n*logn); 空间:O(n);
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct fdata{ double x,y; fdata (double x=0,double y=0):x(x),y(y){}}p[200010];struct hdata{ fdata p,v; double ang; int id; hdata () {} hdata (fdata p,fdata v,int id):p(p),v(v),id(id){ang=atan2(v.y,v.x);} bool operator < (const hdata &b)const{ return ang<b.ang; }}a[200010],que[200010],lne[200010];int n,tot,L,R,ans;fdata operator + (const fdata a,const fdata b) {return fdata(a.x+b.x,a.y+b.y);}fdata operator - (const fdata a,const fdata b) {return fdata(a.x-b.x,a.y-b.y);}fdata operator * (const fdata a,const double x) {return fdata(a.x*x,a.y*x);}double cross(const fdata a,const fdata b) {return a.x*b.y-a.y*b.x;}inline void readi(int &x){ x=0; int f=1; char ch=getchar(); while ('0'>ch||ch>'9') {if (ch=='-') f=-f; ch=getchar();} while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} x*=f;}void _init(){ freopen("archery.in","r",stdin); freopen("archery.out","w",stdout); readi(n); tot=4; a[1]=hdata(fdata(0,1e-15),fdata(1,0),0); a[2]=hdata(fdata(-1e-15,0),fdata(0,1),0); a[3]=hdata(fdata(-1e-15,1e15),fdata(-1,0),0); a[4]=hdata(fdata(-1e15,1e15),fdata(0,-1),0); for (int i=1;i<=n;i++) { int x,y,z; readi(x); readi(y); readi(z); double xx=x,yx=y-1e-15,yy=z+1e-15; a[++tot]=hdata(fdata(0,yx/xx),fdata(1.0/xx,-1),i); a[++tot]=hdata(fdata(0,yy/xx),fdata(-1.0/xx,1),i); } sort(a+1,a+tot+1);}bool pd_lft(fdata p,hdata a){return cross(a.v,p-a.p)>=0;}fdata getJ(hdata a,hdata b){ fdata u=a.p-b.p; double t=cross(b.v,u)/cross(a.v,b.v); return a.v*t+a.p;}bool _check(int tem){ int k=0; for (int i=1;i<=tot;i++) if (a[i].id<=tem) lne[++k]=a[i]; int hed=1,til=1; que[1]=lne[1]; for (int i=2;i<=k;i++){ while (hed<til&&!pd_lft(p[til-1],lne[i])) til--; while (hed<til&&!pd_lft(p[hed],lne[i])) hed++; que[++til]=lne[i]; if (fabs(cross(que[til].v,que[til-1].v))<1e-15){ til--; if (pd_lft(lne[i].p,que[til])) que[til]=lne[i]; } if (hed<til) p[til-1]=getJ(que[til-1],que[til]); } while (hed<til&&!pd_lft(p[til-1],que[hed])) til--; return til-hed>1;}void _solve(){ L=1; R=n; while (L<=R) { int mid=(R-L)/2+L; if (_check(mid)) {ans=mid; L=mid+1;} else R=mid-1; } printf("%d",ans);}int main(){ _init(); _solve(); return 0;}
PS:求管理员解释为什么上一篇博客莫名失踪了。555555……我还得重新写一次。
- BZOJ 2732: [HNOI2012]射箭 题解
- BZOJ 2732: [HNOI2012]射箭
- BZOJ 2732 [HNOI2012]射箭
- bzoj 2732 [HNOI2012]射箭
- BZOJ 2732([HNOI2012]射箭-半平面交)
- BZOJ 2732 [HNOI2012] 射箭 方法简析
- 2732: [HNOI2012]射箭
- bzoj 2732: [HNOI2012]射箭 (二分+半平面交)
- [半平面交 随机增量法] BZOJ 2732 [HNOI2012]射箭
- [HNOI2012]射箭
- 【HNOI2012】射箭
- bzoj 2732 [HNOI2012]射箭 半平面交(刘汝佳版不超时) + 整型二分处理
- 【HNOI2012】【BZOJ2732】射箭
- [bzoj2732][HNOI2012]射箭
- bzoj2732: [HNOI2012]射箭
- bzoj2732【HNOI2012】射箭
- BZOJ2732: [HNOI2012]射箭
- bzoj2732: [HNOI2012]射箭
- 微信小程序项目实践准备工作
- 【量亿数据-量化交易】MACD五日变值法
- 二项式反演,莫比乌斯反演。
- 经典回溯问题-八皇后
- plsql注册码
- BZOJ 2732: [HNOI2012]射箭 题解
- union 与struct的空间计算
- js根据输入身份证信息带出出生日期,年龄,性别
- 7只老鼠测试100个瓶子
- Puzzle(HDU 6048)
- 【Python】nonzero
- AVAudioRecorder 录音
- DOS
- Cron参数解析