【BZOJ2732】【HNOI2012】射箭 二分+半平面交

此题重点在卡精度!!!

本地已经下载数据测试并通过了,然而B站上还是WA的,可能是CPU对于long double 的资瓷不一样。

 

此题答案显然是可以二分出来的,设当前要监测是否能射穿前mid个靶子。

我们发现要穿过第i个靶子,那么a,b必须满足liax2i+bxiri

我们简单地转化下式子,我们就可以得到lix2i+bxiarix2i+bxi

我们想象一个以b为横坐标,a为纵坐标的平面直角坐标系中,这一对约束可以通过两个半平面的交来表示。

不难发现满足射穿前mid个靶子的数对(a,b),必然在这2mid个半平面的交内。

于是问题就转化为这2mid个半平面是否有交(半平面交判定)

时间复杂度:O(n log n)

复制代码
 1 #include<bits/stdc++.h>
 2 #define M 210005
 3 #define D long double
 4 #define eps 1e-20
 5 #define INF 1e15
 6 using namespace std;
 7 
 8 int dx[M]={0},dl[M]={0},dr[M]={0},m=0;
 9 
10 struct pt{
11     D x,y; pt(D X=0,D Y=0){x=X; y=Y;}
12     friend pt operator +(pt a,pt b){return pt(a.x+b.x,a.y+b.y);}
13     friend pt operator -(pt a,pt b){return pt(a.x-b.x,a.y-b.y);}
14     friend D operator *(pt a,pt b){return a.x*b.y-a.y*b.x;}
15     void out(){printf("(%.1Lf,%.1Lf)",x,y);}
16 };
17 struct line{
18     pt a,b; D ang; int id;
19     line(){a=pt(0,0); b=pt(0,0);}
20     line(pt A,pt B,int ID){ id=ID; a=A; b=B; 
21     ang=atan2(b.y-a.y,b.x-a.x);}
22     
23     D getk(){return (b.y-a.y)/(b.x-a.x);}
24     D getb(){return a.y-a.x*getk();}
25     friend bool operator <(line a,line b){return a.ang<b.ang;}
26     friend pt operator *(line a,line b){
27         D k1=(b.b-a.a)*(a.b-a.a);
28         D k2=(a.b-a.a)*(b.a-a.a);
29             D t=k2/(k1+k2);
30         return pt(b.a.x+t*(b.b.x-b.a.x),b.a.y+t*(b.b.y-b.a.y));
31     }
32 }p[M],q[M];
33 bool inleft(line a,line b,line c){
34     pt d=a*b;
35     return (d-c.a)*(c.b-c.a)<=eps;
36 }
37 bool solve(int n){
38     int l=1,r=0;
39     for(int i=1;i<=m;i++){
40         if(p[i].id>n) continue; 
41         while(l<r&&(!inleft(q[r-1],q[r],p[i]))) r--;
42         while(l<r&&(!inleft(q[l+1],q[l],p[i]))) l++;
43         q[++r]=p[i];
44     }
45     while(l<r&&(!inleft(q[r-1],q[r],q[l]))) r--;
46     while(l<r&&(!inleft(q[l+1],q[l],q[r]))) l++;
47     return r-l>=2;
48 }
49 int main(){
50     int n;scanf("%d",&n); if(n<=3){printf("%d\n",n); return 0;}
51     for(int i=1;i<=n;i++) scanf("%d%d%d",dx+i,dl+i,dr+i);
52     int l=1,r=n;
53     for(int i=1;i<=n;i++){
54         D L=dl[i],R=dr[i],X=dx[i];
55         p[++m]=line(pt(0,L/(X*X)),pt(1,L/(X*X)-1/X),i);
56         p[++m]=line(pt(1,R/(X*X)-1/X),pt(0,R/(X*X)),i);
57     }
58     pt a1=pt(INF,INF),a2=pt(-INF,INF),a3=pt(-INF,-INF),a4=pt(INF,-INF);
59     p[++m]=line(a1,a2,0); p[++m]=line(a2,a3,0); p[++m]=line(a3,a4,0); p[++m]=line(a4,a1,0);
60     sort(p+1,p+m+1);
61     while(l<r){
62         int mid=(l+r+1)>>1;
63         if(solve(mid)) l=mid;
64         else r=mid-1;
65     }
66     cout<<l<<endl;
67 }
复制代码

 

posted @   AlphaInf  阅读(154)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示