SGU345 Revolution & Hdu 3684 Gunshots

题目地址:

http://acm.hust.edu.cn:8080/judge/problem/viewProblem.action?id=10551

http://acm.hdu.edu.cn/showproblem.php?pid=3684

大意: 给你一个凸包,然后m个询问,每个询问给你一直线,求该直线将凸包分成两份中,面积最小的是多少?

这道题,自己没有做出来,可以这样来想该问题

(1)该题最关键的地方是求出与凸包的两个交点

(2)由于m很大,对于每个询问必然是log的操作。

(3)看到log,第一反应将会是二分来找这两个点,但是会发现它在整个凸包上并不满足单调性,这怎么办呢?

(4)发挥创造性,各种尝试之后,观察下图:

                                  

观察上图,你会发现  BAE和EDCB这两段上满足单调性(各点到蓝色线的有向距离是单调的)

(1)如果我们已经得到BE两点了,那么通过二分可以得到直线左边最近的点B,直线右边最近的点A,相交点即为直线BA与蓝色线的交点

(2)如何求出BE两点呢?

          观察CD,DE,EA,AB,……你发现这些边一直都是往左偏,因而可以设定极角越来越大,然后你会发现E点的性质,红色线被经过E点的两条线夹住了,即极角位于两者之间,因而这里可以用二分求出B,E两点(通过两次来求这两点)

基本思路已经出来了,一些细节,请读者自己YY了,如果还有疑问,可以参考下面的代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int const N=50005;
double eps=1e-8;
double pi=acos(-1.0);
struct P
{
    double x,y;
    void read(){scanf("%lf%lf",&x,&y);}
    P(){}
    P(double x,double y):x(x),y(y){}
    double operator *(const P& p)const{return x*p.y-y*p.x;}
    P operator -(const P& p)const{return P(x-p.x,y-p.y);}
    P operator +(const P& p)const{return P(x+p.x,y+p.y);}
    P operator *(double const& d)const{return P(x*d,y*d);}
    P operator /(double const& d)const{return P(x/d,y/d);}
    double angle()const{return atan2(y,x);}
    void out(){printf("P: %f %f\n",x,y);}
};
P p[N*2],Q[N];
double sum[N*2];
bool cmp(const P& p1,const P&p2)
{
    if(fabs(p1.y-p2.y)<1e-8)return p1.x<p2.x;
    else return  p1.y<p2.y;
}
int Poly(int n)
{
    sort(p,p+n,cmp);
    int N=0;
    for(int i=0;i<n;i++)
    {
        while(N>=2&&(Q[N-1]-Q[N-2])*(p[i]-Q[N-1])<eps)N--;
        Q[N++]=p[i];
    }
    int t=N+1;
    for(int i=n-2;i>=0;i--)
    {
        while(N>=t&&(Q[N-1]-Q[N-2])*(p[i]-Q[N-1])<eps)N--;
        Q[N++]=p[i];
    }
    N--;
    for(int i=0;i<N;i++)p[i]=Q[i];
    return N;
}
double Avg[N];
int n;
P u,v;
int mid(double s)
{
    if(s<0)s+=2*pi;
    int L=1,R=n,m;
    int r;
    while(L<=R)
    {
        m=(L+R)/2;
        if(Avg[m]>s+eps)R=m-1;
        else if(Avg[m+1]>s-eps)return m;
        else L=m+1;
    }
    return n;
}
P calc(int L,int R,double &s1,double &s2)
{
    if(R<L)R+=n;
    if((u-v)*(p[L]-v)<-eps)swap(u,v);
    if((u-v)*(p[R]-v)> eps)swap(u,v);
    int s=L,e=R,m;
    while(s+1<e)
    {
        m=(s+e)>>1;
        if((u-v)*(p[m]-v)>-eps)s=m;
        else e=m;
    }
    s1+=sum[s]-sum[L];
    s2+=sum[R]-sum[e];
    P r;
    double t1=(p[e]-p[s])*(u-p[s]);
    double t2=(p[s]-p[e])*(v-p[e]);
    r=(u*t2+v*t1)/(t1+t2);
    s1+=p[s]*r;
    s2+=r*p[e];
    return r;
}
double solve()
{
    u.read();v.read();
    int k1,k2;
    k1=mid((u-v).angle());
    k2=mid((v-u).angle());
    double t1=(u-v)*(p[k1]-v);
    double t2=(u-v)*(p[k2]-v);
    if(t1*t2>-eps)return 0.0;
    double s1,s2;
    P a,b;
    s1=s2=0.0;
    a=calc(k1,k2,s1,s2);
    b=calc(k2,k1,s2,s1);
    s1+=a*b;
    s2+=b*a;
    return min(s1,s2)/2.0;
}
int main()
{
   freopen("a.in","r",stdin);
    int m;
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)p[i].read();
        n=Poly(n);
        for(int i=0;i<n;i++)
        p[i+n]=p[i];
        int n2=n*2;
        p[n2]=p[0];
        for(int i=1;i<=n2;i++)
        {
            sum[i]=sum[i-1]+p[i-1]*p[i];
        }

        for(int i=1;i<=n+1;i++)
        {
            Avg[i]=(p[i]-p[i-1]).angle();
            if(Avg[i]<Avg[i-1]-eps)Avg[i]+=2*pi;
        }
        scanf("%d",&m);
        while(m--)
        printf("%.6f\n",solve());

    }


    return 0;
}


      




      



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值