题解[ [HNOI2011]数矩形 ]

题目

Luogu

darkbzoj

Sol

提供一个不用高级计算几何技巧的写法。

感觉和(Atcoder ABC220G)比较相似。

把全部的直线求出来。

考虑两条直线满足什么条件才会构成一个矩形的对边:

  • 两条直线的中垂线完全相同
  • 原本的两条直线不重合
  • 两条直线长度相同

那就好办了:把所有的(dfrac{ncdot(n-1)}{2})条直线根据中垂线排序,同时设直线的中点为这条直线的标志点,两条中垂线相同且长度相同的直线若中点不同则可以构成矩形。

在所有中垂线相同的直线中,找到标志点横坐标的差最大的两条直线,算出面积,最后取最大值。

复杂度(O(n^2log_2n))

如何求点((x_i,y_i))和点((x_j,y_j))所成的直线的中垂线?

((x_i,y_i))​和点((x_j,y_j))​所成的直线的斜率:(k_1=dfrac{y_j-y_i}{x_j-x_i})

所以中垂线的斜率为(k_2=dfrac{-1}{k_1})

再根据中垂线经过中点((dfrac{x_i+x_j}{2},dfrac{y_i+y_j}{2}))即可求出截距(b)

还有一个细节:若中垂线平行于(y)轴,就把(k)设为无穷大,(b)设为(dfrac{x_i+y_j}{2})

Code

#include<bits/stdc++.h>
#define double long double
#define N (1510)
#define L (3000010)
#define ll long long
using namespace std;
struct Point{double x,y;}p[N];
struct Line{double k,b,x,y,l;int type;}ln[L];
int n,tot;
double ans;
inline bool cmp(Line a,Line b){
    //根据中垂线排序
	if(a.type!=b.type) return a.type<b.type;
	if(a.k!=b.k) return a.k<b.k;
	if(a.b!=b.b) return a.b<b.b;
	if(a.l!=b.l) return a.l<b.l;
	return a.x<b.x;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%Lf%Lf",&p[i].x,&p[i].y);
	for(int i=1;i<n;i++){
		for(int j=i+1;j<=n;j++){
            //求中垂线
			ln[++tot].type=0;
			double xi=p[i].x,xj=p[j].x,yi=p[i].y,yj=p[j].y;
			double X=xi-xj,Y=yi-yj;
			double k1=xj-xi,k2=yi-yj;
			double b1=yi*yi-yj*yj+xi*xi-xj*xj,b2=2*(yi-yj);
			ln[tot].x=(xi+xj)/2.0,ln[tot].y=(yi+yj)/2.0;
			ln[tot].l=sqrt(X*X+Y*Y);
			if(k2==0){
				ln[tot].k=1e18;
				ln[tot].type=1;
				ln[tot].b=(xi+xj)/2.0;
				continue;
			}
			double k=k1/k2,b=b1/b2;			
			ln[tot].k=k,ln[tot].b=b;
		}
	}
	sort(ln+1,ln+1+tot,cmp);
	for(int i=1;i<=tot;){
		int j=i+1;
        //找到最远的点j
		while(ln[j].k==ln[i].k&&ln[j].l==ln[i].l&&ln[j].b==ln[i].b&&j<=tot) ++j;
		--j;
		if(i!=j&&(ln[i].x!=ln[j].x||ln[i].y!=ln[j].y)){
			double X=ln[i].x-ln[j].x,Y=ln[i].y-ln[j].y;
			double len=sqrt(X*X+Y*Y);
			double res=ln[i].l*len;
			ans=max(ans,res);
		}
		i=j+1;
	}
	printf("%.0Lf
",ans);
	return 0;
}

完结撒花❀

原文地址:https://www.cnblogs.com/xxbbkk/p/15425202.html