题目地址:
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;
}