BZOJ 2732: [HNOI2012]射箭

首先分析一下题目

设抛物线为y=a*x^2+b*x

然后就是看是否存在a,b满足条件了

即y1<=xi*xi*a+xi*b<=y2

所以就是半平面交了

二分判断一下就……卡精度了QAQ

所以要用long double

不过判断半平面交是否为空集好像有期望O(n)的随机增量法,但是我不会啊

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define double long double
const int N=200000+10;
const double inf=1e15;
int read(){
	char ch=getchar();int x=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct point{
	double x,y;
};
struct line{
	point a,b;
	double k;
	int id;
}l[N];
point operator - (point a,point b){
	return (point){a.x-b.x,a.y-b.y};
}
point operator + (point a,point b){
	return (point){a.x+b.x,a.y+b.y};
}
point operator * (point a,double k){
	return (point){a.x*k,a.y*k};
}
point operator / (point a,double k){
	return (point){a.x/k,a.y/k};
}
double cross(point a,point b){
	return a.x*b.y-a.y*b.x;
}
double cross(point a,point b,point c){
	return cross(b-a,c-a);
}
bool cmp(line i,line j){
	if(i.k==j.k)return cross(i.a,i.b,j.b)>0;
	return i.k<j.k;
}
point getpoint(line l1,line l2){
	double k1=cross(l1.a,l2.b,l1.b),k2=cross(l1.a,l1.b,l2.a),t=k2/(k1+k2);
	return l2.a+((l2.b-l2.a)*t);
}
bool check(line l1,line l2,line l){
	point p=getpoint(l1,l2);
	return cross(l.a,p,l.b)>0;
}
int rk[N],dq[N],ln,bot,top;
bool HPI(int x){
	int j=0;
	for(int i=1;i<=ln;i++)
	if(l[i].id<=x){
		if(l[i].k!=l[rk[j]].k)j++;
		rk[j]=i;
	}
	dq[bot=0]=rk[1];dq[top=1]=rk[2];
	for(int i=3;i<=j;i++){
		while(bot<top&&check(l[dq[top-1]],l[dq[top]],l[rk[i]]))top--;
		while(bot<top&&check(l[dq[bot+1]],l[dq[bot]],l[rk[i]]))bot++;
		dq[++top]=rk[i];
	}
	while(bot<top&&check(l[dq[top-1]],l[dq[top]],l[dq[bot]]))top--;
	while(bot<top&&check(l[dq[bot+1]],l[dq[bot]],l[dq[top]]))bot++;
	return top-bot>1;
}
point getpoint(double x,double y,double xi){
	return (point){x,y/xi-xi*x};
}
int main(){
	//freopen("a.in","r",stdin);
	int n;scanf("%d",&n);
	l[++ln].a=(point){-inf,-inf};l[ln].b=(point){inf,-inf};
	l[++ln].a=(point){inf,-inf};l[ln].b=(point){inf,inf};
	l[++ln].a=(point){inf,inf};l[ln].b=(point){-inf,inf};
	l[++ln].a=(point){-inf,inf};l[ln].b=(point){-inf,-inf};
	for(int i=1;i<=n;i++){
		double x,y1,y2;
		x=read();y1=read();y2=read();
		l[++ln].a=getpoint(1,y2,x);
		l[ln].b=getpoint(-1,y2,x);
		l[++ln].a=getpoint(-1,y1,x);
		l[ln].b=getpoint(1,y1,x);
		l[ln].id=l[ln-1].id=i;
	}
	for(int i=1;i<=ln;i++)l[i].k=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x);
	sort(l+1,l+1+ln,cmp);
	int L=1,R=n;
	while(L<=R){
		int mid=L+R>>1;
		if(HPI(mid))L=mid+1;
		else R=mid-1;
	}
	printf("%d\n",L-1);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值