洛谷P3222 [HNOI2012]射箭(计算几何,半平面交,双端队列)
设抛物线方程为\(y=ax^2+bx(a<0,b>0)\),我们想要求出一组\(a,b\)使得它尽可能满足更多的要求。这个显然可以二分答案。
如何check当前的\(mid\)是否合法呢?每一个限制条件形如\(y_{i_1}\le ax_i^2+bx_i\le y_{i_2}\),也就是\(\frac{y_{i_1}}{x_i}\le x_ia+b\le \frac{y_{i_2}}{x_i}\)。把\(a,b\)看成自变量,实际上每个不等式就是一个半平面,我们需要求出半平面交。
需要掌握向量、叉积等少量基础算法(不过做到这题的大佬们肯定会了),可以参考xzy巨佬的总结。
有一种\(O(n^2)\)的动态插入半平面的做法,可以通过原题数据(目前最优解第一版大部分是这种写法),也可以参考xzy巨佬的总结。
蒟蒻构造了一组边数很多的半平面交,可以卡掉这种写法,目前rank1的代码本机需要20s以上。
一些hack数据可以从这里下(部分转自liu_runda)
链接: https://pan.baidu.com/s/1Te0G-L2JrRu361qKAGorhQ
提取码: ea9m
谈一谈正经的\(O(n\log n)\)的实现吧。以下内容从蒟蒻的总结里㧟的。
我们用有向直线(一个点和一个方向向量)表示半平面,以下默认半平面在有向直线的左侧。
对有向直线按方向向量的极角排序,维护一个双端队列,存储当前构成半平面的直线以及相邻两直线的交点。
每次加入一条有向直线,如果队首/队尾的交点在直线右侧(用叉积判)则弹掉队首/队尾的直线。
为什么这样是对的呢?因为加入直线的单调性,所以要被弹出的直线一定在队首或队尾。感兴趣的话可以自己手画一些例子来理解。
需要注意的细节:
- 加入直线时,先弹队尾,再弹队首。
- 最后还要检查队尾交点是否在队首直线的右侧,如果是也要弹掉。
- 特判平行直线,在右侧的要弹掉。
- 如果题目给出的半平面不一定有限制边界,则应该手动加入一个INF边界。
算法的复杂度瓶颈在排序,因此预先将这些有向直线排好序,二分check时忽略编号大于mid的直线就可以了。时间复杂度\(O(n\log n)\)。
注意这题的坐标范围是\(1e9\)范围,因此INF设到\(1e10\)以上,EPS设到\(1e-10\)以下。
#include<bits/stdc++.h>
#define RG register
#define I inline
#define R RG int
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
using namespace std;
typedef double DB;
const int SZ=1<<19,N=2e5+9;
const DB INF=1e11,EPS=1e-11;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int in(){
G;while(*ip<'-')G;
R x=*ip&15;G;
while(*ip>'-'){x*=10;x+=*ip&15;G;}
return x;
}
struct Vec{
DB x,y;
I Vec(){}
I Vec(DB a,DB b){x=a;y=b;}
I Vec operator+(Vec a){return Vec(x+a.x,y+a.y);}
I Vec operator-(Vec a){return Vec(x-a.x,y-a.y);}
I Vec operator*(DB a){return Vec(x*a,y*a);}//数乘
I DB operator^(Vec a){return x*a.y-y*a.x;}//叉积
}k[N];
struct Line{
Vec p,v;DB ang;int id;
I Line(){}
I Line(Vec a,Vec b,R c){p=a,v=b-a,ang=atan2(v.y,v.x),id=c;}
I bool operator<(Line&a){return ang<a.ang;}
I bool Right(Vec&a){return (v^(a-p))<-EPS;}
I friend Vec Cross(Line&a,Line&b){//求直线交点
return a.p+a.v*((b.v^(b.p-a.p))/(b.v^a.v));
}
}a[N],q[N];
int p=0,l=1,r,mid;
bool HalfPlane(Line*a,Line*e){//求半平面是否有交
R n=e-a,i=0,h=0,t=0;
while(a[i].id>mid)++i;
for(q[0]=a[i++];i<n;++i){
if(a[i].id>mid)continue;
while(h<t&&a[i].Right(k[t-1]))--t;
while(h<t&&a[i].Right(k[h]))++h;
if(a[i].ang!=q[t].ang)q[++t]=a[i];
else if(a[i].Right(q[t].p))q[t]=a[i];
if(h<t)k[t-1]=Cross(q[t-1],q[t]);
}
while(h<t&&q[h].Right(k[t-1]))--t;
return t-h>1;
}
int main(){
r=in();
for(R i=1;i<=r;++i){
DB x=in(),y1=in(),y2=in();
a[++p]=Line(Vec(0,y1/x),Vec(1,y1/x-x),i);
a[++p]=Line(Vec(1,y2/x-x),Vec(0,y2/x),i);
}//边界要设EPS不能设0,因为a、b为0均不合题意
a[++p]=Line(Vec(-INF,EPS),Vec(-EPS,EPS),0);
a[++p]=Line(Vec(-EPS,EPS),Vec(-EPS,INF),0);
a[++p]=Line(Vec(-EPS,INF),Vec(-INF,INF),0);
a[++p]=Line(Vec(-INF,INF),Vec(-INF,EPS),0);
sort(a+1,a+p+1);
while(l<r){
mid=(l+r+1)>>1;
HalfPlane(a+1,a+p+1)?l=mid:r=mid-1;
}
cout<<l<<endl;
return 0;
}
洛谷P3222 [HNOI2012]射箭(计算几何,半平面交,双端队列)的更多相关文章
- 【bzoj2732】[HNOI2012]射箭 二分+半平面交
题目描述 给出二维平面上n个与y轴平行的线段,求最大的k,使得存在一条形如$y=ax^2+bx(a<0,b>0)$的抛物线与前k条线段均有公共点 输入 输入文件第一行是一个正整数N,表示一 ...
- 【BZOJ2732】【HNOI2012】射箭 二分+半平面交
此题重点在卡精度!!! 本地已经下载数据测试并通过了,然而$B$站上还是$WA$的,可能是$CPU$对于$long\ double$ 的资瓷不一样. 此题答案显然是可以二分出来的,设当前要监测是否能射 ...
- 洛谷 - P2283 - 多边形 - 半平面交
https://www.luogu.org/problemnew/show/P2283 需要注意max是求解顺序是从右到左,最好保证安全每次都清空就没问题了. #include<bits/std ...
- 洛谷 - P1663 - 山 - 半平面交
https://www.luogu.org/problemnew/show/P1663 给定山的性状,求一个最低点可以看见所有的地方. 就是半平面交. 粘贴全家福: #include<bits/ ...
- 【kuangbin专题】计算几何_半平面交
1.poj3335 Rotating Scoreboard 传送:http://poj.org/problem?id=3335 题意:就是有个球场,球场的形状是个凸多边形,然后观众是坐在多边形的边上的 ...
- 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交
4515: [Sdoi2016]游戏 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 304 Solved: 129[Submit][Status][ ...
- poj3335 半平面交
题意:给出一多边形.判断多边形是否存在一点,使得多边形边界上的所有点都能看见该点. sol:在纸上随手画画就可以找出规律:按逆时针顺序连接所有点.然后找出这些line的半平面交. 题中给出的点已经按顺 ...
- POJ3525 半平面交
题意:求某凸多边形内部离边界最远的点到边界的距离 首先介绍半平面.半平面交的概念: 半平面:对于一条有向直线,它的方向的左手侧就是它所划定的半平面范围.如图所示: 半平面交:多个半平面的交集.有点类似 ...
- POJ 3130 How I Mathematician Wonder What You Are! /POJ 3335 Rotating Scoreboard 初涉半平面交
题意:逆时针给出N个点,求这个多边形是否有核. 思路:半平面交求多边形是否有核.模板题. 定义: 多边形核:多边形的核可以只是一个点,一条直线,但大多数情况下是一个区域(如果是一个区域则必为 ).核内 ...
随机推荐
- 动态规划-LIS最长上升子序列
优化链接 [https://blog.csdn.net/George__Yu/article/details/75896330] #include<stdio.h> #include< ...
- AddRange 取代 Add
cmd.Parameters的Add方法是增加一个参数,增加多个参数的的时候使用一个foreach循环,cmd.Parameters的AddRange方法是增加一个参数的数组. Add:将指定的对象添 ...
- 【学习总结】Git学习-参考廖雪峰老师教程三-创建版本库
学习总结之Git学习-总 目录: 一.Git简介 二.安装Git 三.创建版本库 四.时光机穿梭 五.远程仓库 六.分支管理 七.标签管理 八.使用GitHub 九.使用码云 十.自定义Git 期末总 ...
- Redis启动及密码修改
.cmd启动Redis: redis-server.exe redis.windows.conf #注意指定配置文件来启动 .cmd登陆redis redis-cli.exe -h -a .修改密码 ...
- centos/ubuntu 双击运行 .sh(shell)文件
centos 创建桌面双击启动程序(更改图标) - Feythin Lau - 博客园http://www.cnblogs.com/feiyuliu/archive/2012/11/29/279503 ...
- 前端三大框架Angular & React & Vue
前端三大框架: Angular[Google]:一套框架,多种平台移动端 & 桌面端.学会用Angular构建应用,然后把这些代码和能力复用在多种多种不同平台的应用上 —— Web.移动 We ...
- 使用jmeter来发送json/gzip格式数据 --------笔记
一.使用jmeter来发送gzip数据 有时候我们需要模拟在客户端将数据压缩后, 发送(post)到服务器端. 通常这种情况,会发生在移动终端上. 这样做的好处, 是可以节省流量. 当然, 服务器返 ...
- 【Python3练习题 019】 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。
后一个分数的分子=前一个分数的分子+分母,后一个分数的分母=前一个分数的分子,循环个20次就有结果.注意,假设分子为a,分母为b,虽然 a = a + b, 但此时a已经变成 a+b 了,所以再给b重 ...
- asp.net core前后端分离
陆陆续续的看了两个礼拜的前端知识,把vue+vue-router+axios的知识撸了一遍,本来想加个element-ui来实现一下前后端分离,实施的时候却遇到了很多的坑.我本身不在一个软件开发公司上 ...
- Velocity之初印象
Velocity 模板引擎介绍 在现今的软件开发过程中,软件开发人员将更多的精力投入在了重复的相似劳动中.特别是在如今特别流行的 MVC 架构模式中,软件各个层次的功能更加独立,同时代码的相似度也更加 ...