SGU345 Revolution & Hdu 3684 Gunshots

来源:互联网 发布:php制作99乘法表 编辑:程序博客网 时间:2024/06/06 01:46

题目地址:

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;}


      




      



原创粉丝点击