原题链接(不讲道理的卡精度):http://www.lydsy.com/JudgeOnline/problem.php?id=2732
洛谷链接(不讲道理的卡时间):https://www.luogu.org/problemnew/show/P3222
射箭
Description
沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。在闯关模式中,第一关只有一个靶 子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。沫沫花了很多时间在这个游戏上,却最多只能玩到第七关“七星连珠”,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关
Input
输入文件第一行是一个正整数N,表示一共有N关。接下来有N行,第i+1行是用空格隔开的三个正整数xi,yi1,yi2(yi1
Output
仅包含一个整数,表示最多的通关数。
Sample Input
5
2 8 12
5 4 5
3 8 10
6 2 3
1 3 7
Sample Output
3
HINT
题解
每个靶子应满足如下不等式:
其中,x为靶子的横坐标, y1,y2 y 1 , y 2 分别为下、上端点纵坐标。
以左半边不等式为例,进行如下变形:
其中, x,y1,y2 x , y 1 , y 2 都是已知量,所以,这事实上是个一次函数。
显然,a是自变量,b是因变量,而上方的不等式组则变成下列形式:
如此一来,这个问题就转化为半平面交问题,
b⩾−ax+y1x
b
⩾
−
a
x
+
y
1
x
表示这条直线以上的半平面,同理
b⩽−ax+y2x
b
⩽
−
a
x
+
y
2
x
表示直线一下的半平面,我们只需要判定这些半平面交集是否为空即可。
代码实现时,只需要调整一下结构体中起始点的顺序,就可以将“上下”两个方向转化为有向直线的左或右单一方向的问题。半平面交时,我们需要一个双端队列,每次检查队尾的两条直线的交点是否在新加入直线的右边,如果是,则弹出队尾元素,对于队首,我们需要进行同样的操作。所有元素添加完毕之后,还需要检查一遍首尾元素之间是否符合要求。最后,如果队列中剩余两个及以上的元素,那么半平面交存在。
为了求最多能过多少关,我们还需要一个二分枚举关卡。
坑
在洛谷上,没有卡精度,eps取零即可,然而需要用long double,不过很容易T(卡常不讲道理),博主靠洛谷O2优化苟过。
而BZOJ与之相反,不卡常,卡精度,取了许多eps都没过,弃疗。。。
代码
#include<bits/stdc++.h>
#define db long double
using namespace std;
const int M=2e5+5;
const int inf=1e9;
const db eps=0;
int n,tot,top;
struct pt{db x,y;};
struct li{pt x1,x2;db k;int id;};
int sig(db x){return (x>eps)-(x<-eps);}
pt operator *(pt a,db b){return (pt){a.x*b,a.y*b};}
pt operator /(pt a,db b){return (pt){a.x/b,a.y/b};}
pt operator +(pt a,pt b){return (pt){a.x+b.x,a.y+b.y};}
db operator *(pt a,pt b){return a.x*b.y-a.y*b.x;}
pt operator -(pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
db cal(db a,db b,db x){return b/a-a*x;}
bool operator <(li a,li b){return !sig(a.k-b.k)?sig((a.x2-a.x1)*(b.x1-a.x1))>0:a.k<b.k;}
li line[M],que[M],p[M];
int r,f;
char c;
int read()
{
r=0;f=1;
c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))r=(r<<1)+(r<<3)+c-'0',c=getchar();
return r*f;
}
void in()
{
line[++tot].x1=(pt){-inf,-inf};line[tot].x2=(pt){inf,-inf};
line[++tot].x1=(pt){inf,-inf};line[tot].x2=(pt){inf,inf};
line[++tot].x1=(pt){inf,inf};line[tot].x2=(pt){-inf,inf};
line[++tot].x1=(pt){-inf,inf};line[tot].x2=(pt){-inf,-inf};
n=read();
db x,y1,y2;
for(int i=1;i<=n;++i)
{
x=read();y1=read();y2=read();
line[++tot].x1=(pt){-1000,cal(x,y1,-1000)};
line[tot].x2=(pt){1000,cal(x,y1,1000)};
line[++tot].x1=(pt){1000,cal(x,y2,1000)};
line[tot].x2=(pt){-1000,cal(x,y2,-1000)};
line[tot].id=line[tot-1].id=i;
}
}
pt inter(li a,li b)
{
db k1,k2;
k1=(b.x2-a.x1)*(a.x2-a.x1);
k2=(a.x2-a.x1)*(b.x1-a.x1);
return (b.x2-b.x1)*k2/(k1+k2)+b.x1;
}
bool check(li a,li b,li t)
{
pt p=inter(a,b);
return sig((p-t.x1)*(t.x2-t.x1))>0;
}
bool hpi(int x)
{
top=0;
for(int i=1;i<=tot;++i)
{
if(line[i].id<=x)
{
if(line[i].k!=p[top].k)++top;
p[top]=line[i];
}
}
int le=1,ri=0;
que[++ri]=p[1];que[++ri]=p[2];
for(int i=3;i<=top;++i)
{
while(le<ri&&check(que[ri-1],que[ri],p[i]))--ri;
while(le<ri&&check(que[le+1],que[le],p[i]))++le;
que[++ri]=p[i];
}
while(le<ri&&check(que[ri-1],que[ri],que[le]))--ri;
while(le<ri&&check(que[le+1],que[le],que[ri]))++le;
return (ri-le)>=2;
}
void ac()
{
for(int i=1;i<=tot;++i)
line[i].k=atan2(line[i].x2.y-line[i].x1.y,line[i].x2.x-line[i].x1.x);
sort(line+1,line+1+tot);
int le=1,ri=n,ans=0,mid;
while(le<=ri)
{
mid=(le+ri)>>1;
if(hpi(mid))ans=mid,le=mid+1;
else ri=mid-1;
}
printf("%d",ans);
}
int main()
{
in();ac();
return 0;
}