CF1578F Framing Pictures 旋转卡壳+积分

CF1578F Framing Pictures 旋转卡壳+积分

Framing Pictures

题目描述

Life has been discovered on Venus! What is more, the life forms appear to be convex polygons. An international consortium is designing a probe to send to Venus to take pictures, but they need to estimate the bandwidth needed to send back pictures.

When the probe takes a picture of a life form and wishes to send it back to Earth, the bandwidth required is proportional to the area of the bounding box (in other words, the smallest axis-aligned rectangle that contains the life-form). The shape and size of the life forms are known, but the orientation relative to the camera is random. You must thus determine the expected (average) area of the bounding box across all orientations.

输入格式

The input describes the shape of a life form as a convex polygon in two dimensions.

The first line of input contains an integer $ n $ ( $ 3 \le n \le 200,000 $ ) — the number of vertices. The remaining $ n $ lines each contain two integers $ x $ and $ y $ ( $ -10^9 \le x, y \le 10^9 $ ) — the coordinates of a vertex. The vertices are given in counterclockwise order, and no three vertices lie on a straight line.

输出格式

Output a single line containing the expected area of the bounding box of the polygon. Your answer should have an absolute or relative error of at most $ 10^{-6} $ .

样例 #1

样例输入 #1

4
0 0
10 0
10 10
0 10

样例输出 #1

163.661977237

样例 #2

样例输入 #2

5
0 0
10 0
15 8
5 20
-5 7

样例输出 #2

365.666028588

提示

The pictures show example life forms and various camera orientations.

假设用一个可以旋转的矩形边框去卡住图形,根据旋转的角度不同,边框的形状随时都在变化。假设我们选择任意一个角度的概率相等,要求的就是边框的面积期望。所以对旋转角度求边框面积的积分即可。


不难发现,如果角度确定,那么边框会由4个点卡住,我把它分别叫up,down,left,right。随着角度的增大,这四个点会在凸包上逆时针移动。我们发现对于一段角度ang1到ang2,这四个点不会变,对于这一段角度的积分是可以算的。假设前角度为x
u表示down到up的向量。v表示left到right的向量。边框的高可表示为(cosxu.y-sinxu.x)宽可以表示为(cosxv.x+sinxv.y)
面积为(cosxu.y-sinxu.x)(cosxv.x+sinx*v.y)
对面积在ang1到ang2上积分即可。

//#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
//#define double long double
const double pi=acos(-1.0);
const double esp=1e-8;
const double inf=1e100;
bool cmp0(double x){return fabs(x)<esp;}
int cmp(double x){return cmp0(x)?0:x>0?1:-1;}
double acs(double x){if(cmp0(x-1))x=1;if(cmp0(x+1))x=-1;return acos(x);}
struct point{//点/向量 
	double x,y;
	void in(){scanf("%lf %lf",&x,&y);}
	void out(){printf("%lf %lf\n",x,y);}
	point(double xx=0,double yy=0){x=xx,y=yy;}
	point operator + (const point& a)const{return point(x+a.x,y+a.y);}
	point operator - (const point& a)const{return point(x-a.x,y-a.y);}
	point operator * (const double& a)const{return point(x*a,y*a);}
	point operator / (const double& a)const{return point(x/a,y/a);}
	double operator *(const point& a)const{return x*a.x+y*a.y;}
	double operator ^(const point& a)const{return x*a.y-y*a.x;}
	bool operator < (const point& a)const{return x==a.x?y<a.y:x<a.x;}
};
double len1(const point& a){return sqrt(a*a);} //向量模长 
double len2(const point& a){return a*a;}//向量模长平方 
bool cmp1(const point& a,const point& b){return a.x==b.x?a.y<b.y:a.x<b.x;}//坐标排序 
bool cmp2(const point& a,const point& b){return a.y==b.y?a.x<b.x:a.y<b.y;}//坐标排序
double cro(const point& a,const point& b,const point& o){return (a-o)^(b-o);} //叉积 
double dot(const point& a,const point& b,const point& o){return (a-o)*(b-o);}
double rad(point a){a=a/len1(a);return a.y>0?acos(a.x):-acos(a.x);}//向量弧度 
struct poly{//多边形 
	vector<point> p;
	int pos;//最右点 
	poly(){p.reserve(100005);}
};
double f(double x,double dx1,double dx2,double dy1,double dy2)
{
	return dy1*dx2*(2*x+sin(2*x))/4-
		(dy1*dy2-dx1*dx2)*cos(2*x)/4-
		(dx1*dy2)*(2*x-sin(2*x))/4;
}
double cal(double st,double ed,double dx1,double dx2,double dy1,double dy2)
{
	return f(ed,dx1,dx2,dy1,dy2)-f(st,dx1,dx2,dy1,dy2);
}
poly a;
int main()
{
	int n;scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		point p;p.in();
		a.p.push_back(p);
	}
	int up,down,right,left;
	up=1,down=1;
	while(1)
	{
		if(cro(a.p[(up+1)%n],a.p[0],a.p[1])>=cro(a.p[up],a.p[0],a.p[1])) up=(up+1)%n;
		else break;
	}
	left=up;
	while(1)
	{
		if(dot(a.p[(right+1)%n],a.p[1],a.p[0])>=dot(a.p[right],a.p[1],a.p[0])) right=(right+1)%n;
		else break;
	}
	while(1)
	{
		if(dot(a.p[(left+1)%n],a.p[0],a.p[1])>=dot(a.p[left],a.p[0],a.p[1])) left=(left+1)%n;
		else break;
	}	
	double alpha;
	alpha=rad(a.p[1]-a.p[0]);
	double angu,angd,angl,angr;
	double ans=0,now=0;
	while(1)
	{	
		angd=rad(a.p[(down+1)%n]-a.p[down%n]);
		angu=rad(a.p[(up+1)%n]-a.p[up%n])-pi;if(angu<-pi+esp) angu+=2*pi;
		angl=rad(a.p[(left+1)%n]-a.p[left%n])-3*pi/2;if(angl<-pi+esp) angl+=2*pi;
		angr=rad(a.p[(right+1)%n]-a.p[right%n])-pi/2;if(angr<-pi+esp) angr+=2*pi;
		double dd,du,dl,dr,c=10;
		dd=angd-alpha;if(dd<0) dd+=2*pi;c=min(c,dd);
		du=angu-alpha;if(du<0) du+=2*pi;c=min(c,du);
		dl=angl-alpha;if(dl<0) dl+=2*pi;c=min(c,dl);
		dr=angr-alpha;if(dr<0) dr+=2*pi;c=min(c,dr);
		//		
		point u=a.p[up]-a.p[down];
		point v=a.p[right]-a.p[left];
		//
		ans+=cal(alpha,alpha+c,u.x,v.x,u.y,v.y);
		alpha=alpha+c;
		if(alpha>pi) alpha-=2*pi;		
		if(c==dd) down=(down+1)%n;
		if(c==du) up=(up+1)%n;
		if(c==dl) left=(left+1)%n;
		if(c==dr) right=(right+1)%n;
		now=now+c;
		if(now>=pi*2-esp) break;
	}
	printf("%.12lf",ans/pi/2);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值