bzoj2338: [HNOI2011]数矩形

来源:互联网 发布:烽火路由器mac地址过滤 编辑:程序博客网 时间:2024/06/05 18:31

题解

  这题需要开一点脑洞,我只想到了长度相等但是并没有想到中点重合..
  首先进行O(n2)的预处理,处理出n(n1)2条线段作为备选对角线。
  如果两条线段中点重合且长度相等,它们就能组成矩形
  证明的话,我想想
  可以利用直角三角形的中线等于斜边的一半,根据初中几何,这玩意的逆定理也是成立的。然后证明就简单了,我就不写了。
  然后你就按照长度为第一关键字,中点为第二关键字进行排序。
  排序后每条线段暴力往前扫描所有和它长度相等且中点重合的对角线,叉积统计下答案。
  这样复杂度不会退化吗?
  蒟蒻并不会证明,但是蒟蒻发现如果故意构造一些长度相等且中点重合的线段,也就是菊花状,那么长度相等且中点重合的线段的组的大小就会变大,但同时组数会减少。
  如果强行增大组数,那么每一组的大小就会变少。
  我们设组的大小是size,那么组数就是n2size
  程序的复杂度的一个非常宽松的上界显然是O(size2n2size)=O(size×n2)
  想要最大化这个数,显然size要加到最大,但是刚才已经讨论过了,size最大的时候就是N,而且这个时候就是菊花图,菊花图的复杂度已经知道是O(N2)
  因此直觉告诉我这个算法不会太慢

代码

//计算几何 #include <cstdio>#include <algorithm>#define maxn 1500using namespace std;typedef long long ll;ll N, x[maxn], y[maxn];struct segment{ll len, x, y, id1, id2;}seg[maxn*maxn];bool operator<(segment l1, segment l2){    if(l1.len==l2.len)    {        if(l1.x==l2.x)return l1.y<l2.y;        return l1.x<l2.x;    }    return l1.len<l2.len;}bool cmp(segment l1, segment l2){return l1.x==l2.x and l1.y==l2.y and l1.len==l2.len;}ll gets(segment l1, segment l2){    ll x1=x[l1.id1]-x[l1.id2], y1=y[l1.id1]-y[l1.id2], x2=x[l2.id1]-x[l2.id2],       y2=y[l2.id1]-y[l2.id2];    return abs(x1*y2-x2*y1);}ll sqr(ll x){return x*x;}int main(){    freopen("in.txt","r",stdin);    ll i, j, ans=0, tot=0;    scanf("%lld",&N);    for(i=1;i<=N;i++)scanf("%lld%lld",x+i,y+i);    for(i=1;i<=N;i++)for(j=i+1;j<=N;j++)    {        tot++;        seg[tot].id1=i, seg[tot].id2=j;        seg[tot].x=x[i]+x[j], seg[tot].y=y[i]+y[j];        seg[tot].len=sqr(x[i]-x[j])+sqr(y[i]-y[j]);    }    sort(seg+1,seg+tot+1);    for(i=1;i<=tot;i++)        for(j=i-1;j and cmp(seg[j],seg[i]);j--)        {            ans=max(ans,gets(seg[j],seg[i]));        }    printf("%lld",ans>>1);    return 0;}
0 0