题解[ [HNOI2011]数矩形 ]
题目
Sol
提供一个不用高级计算几何技巧的写法。
感觉和\(Atcoder\ ABC220G\)比较相似。
把全部的直线求出来。
考虑两条直线满足什么条件才会构成一个矩形的对边:
- 两条直线的中垂线完全相同
- 原本的两条直线不重合
- 两条直线长度相同
那就好办了:把所有的\(\dfrac{n\cdot(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\n",ans);
return 0;
}