洛谷传送门
BZOJ传送门
LOJ传送门
解析:
其实这道题本身并不毒瘤,但是我看了一下其他题解,发现有的代码实现是真的复杂。。。
这道题其实可以完全不用三角函数做(但是我半平面交习惯先求积极角后排序就用了atan2)。
大胆猜想这种求满足某种条件的区域面积的问题就是半平面交了。
显然我们可以枚举多边形的每条边与第一条边进行比较,显然令两个三角形面积相等的点是有轨迹的,我们尝试求出这个轨迹,靠近第一条边的一边就是合法区域。
如果这个轨迹是直线我们就可以上半平面交了。
现在考虑点 P 1 , P 2 , P i , P i + 1 P_1,P_2,P_{i},P_{i+1} P1,P2,Pi,Pi+1,动点为 P P P,接下来的 x , y x,y x,y如果有下标则表示对应点的 x , y x,y x,y,如果没有则表示 P P P的。
我们要求的区域即 ( P 1 − P ) × ( P 2 − P ) ≤ ( P i − P ) × ( P i + 1 − P ) (P_1-P)\times(P_2-P)\leq (P_i-P)\times (P_{i+1}-P) (P1−P)×(P2−P)≤(Pi−P)×(Pi+1−P)
暴力拆开,合并同类项(这些我就不写了,太长了)之后得到:
P 1 × P 2 − P i × P i + 1 ≤ x ( y i − y i + 1 − y 1 + y 2 ) − y ( x i − x i + 1 − x 1 + x 2 ) P_1\times P_2-P_i\times P_{i+1}\leq x(y_i-y_{i+1}-y_1+y_2)-y(x_i-x_{i+1}-x_1+x_2) P1×P2−Pi×Pi+1≤x(yi−yi+1−y1+y2)−y(xi−xi+1−x1+x2)
将常量全部求出后我们要求的实际上就是这个东西:
c ≤ a x + b y c\leq ax+by c≤ax+by
想必来写这道题的各位都知道这个怎么转成半平面。
把所有限制和多边形的边加入半平面集后做半平面交就好了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
re bool f=0;
while(!isdigit(c=gc()))f^=c=='-';re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
using std::cout;
using std::cerr;
cs int N=1e5+5;
cs double eps=1e-9;
inline int sign(cs double &x){return (int)(x>-eps)-(x<eps);}
struct Point{
double x,y;
Point(){}
Point(cs double &_x,cs double &_y):x(_x),y(_y){}
friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
friend Point operator*(cs Point &a,cs double &b){return Point(a.x*b,a.y*b);}
friend double operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
}p[N];
struct Line{
Point s,e;
double rad;
Line(){}
Line(cs Point &a,cs Point &b):s(a),e(b),rad(atan2(e.y-s.y,e.x-s.x)){}
friend bool operator<(cs Line &a,cs Line &b){
return sign(a.rad-b.rad)?sign(a.rad-b.rad)<0:sign((a.e-a.s)*(b.e-a.s))>0;
}
}l[N<<1],q[N<<1];
inline Point inter(cs Line &a,cs Line &b){
double s1=(a.e-a.s)*(b.s-a.s),s2=(b.e-a.s)*(a.e-a.s);
double k=s1/(s1+s2);
return b.s+(b.e-b.s)*k;
}
inline bool judge(cs Line &a,cs Line &b,cs Line &c){
return sign((c.e-c.s)*(inter(a,b)-c.s))<0;
}
int n,m,tot,head,tail;
inline void Half_plane_Inter(){
std::sort(l+1,l+m+1);n=0;
for(int re i=1;i<=m;++i){
if(i==1||sign(l[i].rad-l[i-1].rad))++n;
l[n]=l[i];
}
m=n;
q[head=1]=l[1];
q[tail=2]=l[2];
for(int re i=3;i<=m;++i){
while(head<tail&&judge(q[tail-1],q[tail],l[i]))--tail;
while(head<tail&&judge(q[head+1],q[head],l[i]))++head;
q[++tail]=l[i];
}
while(head<tail&&judge(q[tail-1],q[tail],q[head]))--tail;
while(head<tail&&judge(q[head+1],q[head],q[tail]))++head;
n=0;
q[tail+1]=q[head];
for(int re i=head;i<=tail;++i)p[++n]=inter(q[i],q[i+1]);
}
double all,area;
signed main(){
n=getint();
for(int re i=1;i<=n;++i)p[i].x=getint(),p[i].y=getint();
p[n+1]=p[1];
for(int re i=1;i<=n;++i){
l[++m]=Line(p[i],p[i+1]);
all+=p[i]*p[i+1];
if(i>1){
double a=p[1]*p[2]-p[i]*p[i+1];
double b=p[i].y-p[i+1].y-p[1].y+p[2].y;
double c=p[i+1].x-p[i].x-p[2].x+p[1].x;
//a<=bx+cy
Point st=Point(sign(c)?0:a/b,sign(c)?a/c:0);
l[++m]=Line(st,st+Point(c,-b));
}
}
Half_plane_Inter();
p[n+1]=p[1];
for(int re i=1;i<=n;++i)area+=p[i]*p[i+1];
printf("%.4f",fabs(area/all));
return 0;
}