[半平面交 随机增量法] BZOJ 2732 [HNOI2012]射箭

设抛物线为 y=ax2+by
那么一个限制

y1<=ax2+by<=y2

转化为
b<=ax+y2xb>=ax+y1x

然后就是一个关于a b的半平面交
这个直接上模板就行了 据说加强的数据是一些半平面交是点 直线 线段 射线 之类的边界情况
我long double卡精度卡到1e-14就过了
还有一个问题就是这个抛物线应该满足 a<0 才符合生活实际 不知道数据有没有考虑到

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long double ld;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(ld &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int sgn(ld a){
  if (fabs(a)<1e-14) return 0; if (a<0) return -1; return 1;
}

const int N=200005;
const ld PI=acos(-1.0);

struct P{
  ld x,y;
  void read() { double _x,_y; scanf("%lf%lf",&_x,&_y); x=_x; y=_y; }
  P(ld x=0,ld y=0):x(x),y(y) { }
  P rot(ld A){ return P(x*cos(A)-y*sin(A),x*sin(A)+y*cos(A)); }
  ld dist(){ return sqrt(x*x+y*y); }
  friend P operator + (P A,P B) { return P(A.x+B.x,A.y+B.y); }
  friend P operator - (P A,P B) { return P(A.x-B.x,A.y-B.y); }
  friend ld operator * (P A,P B) { return A.x*B.y-B.x*A.y; }
  friend P operator * (P A,ld B) { return P(A.x*B,A.y*B); }
  friend P operator / (P A,ld B) { return P(A.x/B,A.y/B); }
  friend ld dist(P A,P B){ return (A-B).dist(); }
}p[N];

struct Tl{
  P p1,p2; int idx; ld ang;
  Tl() { }
  Tl(P _p1,P _p2,int id) { p1=_p1; p2=_p2; idx=id; ang=atan2(p2.y-p1.y,p2.x-p1.x); }
  friend bool operator < (const Tl &A,const Tl &B){
    return sgn(A.ang-B.ang)==0?sgn((A.p1-B.p1)*(B.p2-B.p1))<0:sgn(A.ang-B.ang)<0;
  }
  friend P cross(const Tl &A,const Tl &B){
    ld ta=(A.p2-B.p1)*(A.p1-B.p1),tb=(A.p1-B.p2)*(A.p2-B.p2);
    return B.p1*(tb/(ta+tb))+B.p2*(ta/(ta+tb));
  }
}L[N],line[N]; int tot,Tot;

inline bool onleft(Tl& l1,Tl &l2,Tl &l3){
  P tp=cross(l1,l2); 
  return sgn((tp-l3.p1)*(l3.p2-l3.p1))<=0;
}

int Q[N],l,r;

inline bool hpi(){
  l=1,r=0; Q[++r]=1;
  for (int i=2;i<=tot;i++){
    while (l<r && !onleft(L[Q[r-1]],L[Q[r]],L[i])) 
      r--;
    while (l<r && !onleft(L[Q[l+1]],L[Q[l]],L[i])) 
      l++;
    Q[++r]=i;
  }
  while (l<r && !onleft(L[Q[r-1]],L[Q[r]],L[Q[l]])) r--;
  while (l<r && !onleft(L[Q[l+1]],L[Q[l]],L[Q[r]])) l++;
  return r-l>=2;
}

int n;
#define calc(a,b,x) ((b)/(a)-(a)*(x))

inline bool check(int mid){
  tot=0; L[tot].ang=1e20;
  for (int i=1;i<=Tot;i++)
    if (line[i].idx<=mid)
      if (sgn(line[i].ang-L[tot].ang))
    L[++tot]=line[i];
  return hpi();
}
#define oo 1e15
int main(){
  ld x,y1,y2;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n);
  line[++Tot]=Tl(P(-oo,-oo),P(oo,-oo),0);
  line[++Tot]=Tl(P(oo,-oo),P(oo,oo),0);
  line[++Tot]=Tl(P(oo,oo),P(-oo,oo),0);
  line[++Tot]=Tl(P(-oo,oo),P(-oo,-oo),0);
  for(int i=1;i<=n;i++){
    read(x); read(y1); read(y2);
    line[++Tot]=Tl(P(-1,calc(x,y1,-1)),P(1,calc(x,y1,1)),i);
    line[++Tot]=Tl(P(1,calc(x,y2,1)),P(-1,calc(x,y2,-1)),i);
  }
  sort(line+1,line+Tot+1);
  int L=0,R=n+1,MID;
  while (L+1<R)
    if (check(MID=(L+R)>>1))
      L=MID;
    else
      R=MID;
  printf("%d\n",L);
  return 0;
}

然后因为这里就只是要求判断交是否为空 我们可以上随机增量法
期望复杂度是 O(n) 的 证明同其他应用随机增量法的题
上一张图 来自 随机增量算法 解轶伦
这里写图片描述
这次我卡精度卡到1e-19?

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long double ld;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int sgn(ld a){
  if (fabs(a)<1e-19) return 0; if (a<0) return -1; return 1;
}

const int N=200005;
const ld PI=acos(-1.0);

struct P{
  ld x,y;
  P(ld x=0,ld y=0):x(x),y(y) { }
}p[N];

struct Tl{
  ld k,b; int dir; // 0 : <=  ; 1 : >=
  Tl(ld k=0,ld b=0,int dir=0):k(k),b(b),dir(dir) { }
  P calc(ld x){ return P(x,k*x+b); }
  bool jud(P p){
    if (dir==0) return sgn(p.y-k*p.x-b)<=0;
    else return sgn(p.y-k*p.x-b)>=0;
  }
  friend ld crossx(Tl A,Tl B){ return (B.b-A.b)/(A.k-B.k); }
  friend P cross(Tl A,Tl B){ return A.calc(crossx(A,B)); }
}L[N]; int tot;

int n,xs[N],ys1[N],ys2[N];

int main(){
  ld x,y1,y2;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n);
  for (int i=1;i<=n;i++) read(xs[i]),read(ys1[i]),read(ys2[i]);
  tot=0;
  for (int i=1;i<=n;i++){
    L[++tot]=Tl(-xs[i],(double)ys1[i]/xs[i],1);
    L[++tot]=Tl(-xs[i],(double)ys2[i]/xs[i],0);
  }
  P p=L[1].calc(1);
  for (int i=2;i<=tot;i++)
    if (!L[i].jud(p)){
      ld l=-1e15,r=1e15,t;
      for (int j=1;j<i;j++){
    if (sgn(L[i].k-L[j].k)==0) continue;
    t=crossx(L[j],L[i]);
    if (L[j].jud(L[i].calc(t+1))) l=max(l,t);
    if (L[j].jud(L[i].calc(t-1))) r=min(r,t);
      }
      if (sgn(l-r)>0) {
    printf("%d\n",(i+1)/2-1);
    return 0;
      }
      if (l>-1e15 && r<1e15)
    p=L[i].calc((l+r)/2);
      else if (l>-1e15)
    p=L[i].calc(l);
      else
    p=L[i].calc(r);
    }
  printf("%d\n",n);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值