HDU5462 Manors【半平面交】

題目描述:

HDU5462 Manors

nn個人,每個人mm個旗子座標爲 xi,j,yi,jx_{i,j},y_{i,j}fi(x,y)=j=1m(xxi,j)2+(yyi,j)2f_i(x,y)=\sum_{j=1}^m(x-x_{i,j})^2+(y-y_{i,j})^2

(x,y)(x,y)fi(x,y)f_i(x,y)最小的 ii 佔領,x,yx,y的範圍爲[0,4095][0,4095],求每個人的佔領面積。

n100n\le100

題目分析:

sxi=j=1mxi,jsx_i=\sum_{j=1}^mx_{i,j}s2xi=j=1mxi,j2s_2x_i=\sum_{j=1}^mx_{i,j}^2yy同理。

fi(x,y)fj(x,y)s2xis2xj+s2yis2yj2(sxisxj)x2(syisyj)y0f_i(x,y)\le f_j(x,y)\Longleftrightarrow s_2x_i-s_2x_j+s_2y_i-s_2y_j-2(sx_i-sx_j)x-2(sy_i-sy_j)y\le 0

相當於對於滿足 Ax+By+C0Ax+By+C\le 0x,yx,yfi(x,y)fj(x,y)f_i(x,y)\le f_j(x,y)。那麼 ii 的佔領面積相當於半平面交。

確定半平面交直線方向向量的方法:

在直線的法向量 (A,B)(A,B) 一邊的點一定滿足 Ax+By+C>0Ax'+By'+C>0
(對於任意點x,yx,y滿足Ax+By+C=0Ax+By+C=0,有A(x+A)+B(x+B)+C>0A(x+A)+B(x+B)+C>0

如果令半平面交取左手邊(逆時針),那麼 <0<0 對應的方向向量就是法向量逆時針轉 90°90\degree,即 (B,A)(-B,A)

然後再解出一個滿足Ax+By+C=0Ax+By+C=0(x,y)(x,y) 就可以確定這條直線了。

半平面交一次轉超過180°180\degree隨便舉反例,要在外面框個矩形,每次最多轉90°90\degree,最後少於3條線就無解。

舉幾個例子加深理解:
在這裏插入圖片描述
在這裏插入圖片描述

Code:

#include<bits/stdc++.h>
#define maxn 115
using namespace std;
const double eps = 1e-6;
int T,n,m;
double sx[maxn],sy[maxn],sx2[maxn],sy2[maxn];
int sgn(double x){return x>eps?1:x<-eps?-1:0;}
struct Pt{
	double x,y;
	Pt(double x=0,double y=0):x(x),y(y){}
	Pt operator + (const Pt &p)const{return Pt(x+p.x,y+p.y);}
	Pt operator - (const Pt &p)const{return Pt(x-p.x,y-p.y);}
	Pt operator * (const double &t)const{return Pt(x*t,y*t);}
	double operator * (const Pt &p)const{return x*p.y-y*p.x;}
}p[maxn];
struct Line{
	Pt p,v; double ang;
	Line(Pt p=0,Pt v=0):p(p),v(v),ang(atan2(v.y,v.x)){}
	bool operator < (const Line &L)const{return sgn(ang-L.ang)?ang<L.ang:v*(L.p-p)<=0;}
}L[maxn],q[maxn];
int cnt,l,r;
bool Onleft(Pt p,Line L){return L.v*(p-L.p)>0;}
Pt Inter(Line a,Line b){return a.p+a.v*((b.v*(a.p-b.p))/(a.v*b.v));}
double HalfPlane(){
	sort(L+1,L+1+cnt);
	q[l=r=1]=L[1];
	for(int i=2;i<=cnt;i++) if(sgn(L[i].ang-L[i-1].ang)){
		while(l<r&&!Onleft(p[r-1],L[i])) r--;
		while(l<r&&!Onleft(p[l],L[i])) l++;
		q[++r]=L[i],p[r-1]=Inter(q[r-1],q[r]);
	}
	while(l<r-1&&!Onleft(p[r-1],q[l])) r--;
	if(r-l<=1) return 0;
	p[r]=Inter(q[l],q[r]);
	double ans=0;
	for(int i=l;i<=r;i++) ans+=p[i]*p[i+1>r?l:i+1];
	return ans/2;
}
int main()
{
	scanf("%d",&T);
	for(int t=1;t<=T;t++){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) sx[i]=sy[i]=sx2[i]=sy2[i]=0;
		for(int i=1,x,y;i<=n;i++) for(int j=1;j<=m;j++){
			scanf("%d%d",&x,&y);
			sx[i]+=x,sy[i]+=y,sx2[i]+=x*x,sy2[i]+=y*y;
		}
		printf("Case #%d: ",t);
		for(int i=1;i<=n;i++){
			L[cnt=1]=Line(Pt(0,0),Pt(1,0)),L[++cnt]=Line(Pt(4095,0),Pt(0,1));
			L[++cnt]=Line(Pt(4095,4095),Pt(-1,0)),L[++cnt]=Line(Pt(0,4095),Pt(0,-1));
			for(int j=1;j<=n;j++) if(i!=j){
				double A=-2*(sx[i]-sx[j]),B=-2*(sy[i]-sy[j]),C=sx2[i]-sx2[j]+sy2[i]-sy2[j];
				if(sgn(A)) L[++cnt]=Line(Pt(-C/A,0),Pt(-B,A));
				else L[++cnt]=Line(Pt(0,-C/B),Pt(-B,A));
			}
			printf("%d%c",(int)round(HalfPlane()),i==n?10:32);
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章