1. 程式人生 > >LA4728 Squares

LA4728 Squares

題意

PDF

分析

就是求凸包點集的直徑。

當然選擇旋轉卡殼。

然後是實現上的技巧:

當Area(p[u], p[u+1], p[v+1]) <= Area(p[u], p[u+1], p[v])時停止旋轉
即Cross(p[u+1]-p[u], p[v+1]-p[u]) - Cross(p[u+1]-p[u], p[v]-p[u]) <= 0
根據Cross(A,B) - Cross(A,C) = Cross(A,B-C)
化簡得Cross(p[u+1]-p[u], p[v+1]-p[v]) <= 0

畫個圖就能發現,這樣找的是對於一條邊三角形的最大高。為什麼這樣是對的呢?

凸包上一個點到其他點的距離是一個凸函式。然後在兩條直線慢慢旋轉的過程中,可以考慮直接轉一條邊,這樣求出的是到這條直線的最大距離,顯然就是對踵點對。

實現的時候初始化可以直接暴力轉,因為均攤是\(O(n)\)的。

時間複雜度\(O(T n \log n)\)

程式碼

由於座標都是整數,而又不涉及需要實數運算的操作,所以Point類可以直接實現為整數座標。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>T read(T&x)
{
    return x=read<T>();
}
using namespace std;
typedef long long ll;

struct Point
{
    int x,y;
    
    Point(int x=0,int y=0)
    :x(x),y(y){}
    
    bool operator<(co Point&rhs)co
    {
        return x<rhs.x||(x==rhs.x&&y<rhs.y);
    }
    
    bool operator==(co Point&rhs)co
    {
        return x==rhs.x&&y==rhs.y;
    }
};
typedef Point Vector;

Vector operator-(co Point&A,co Point&B)
{
    return Vector(A.x-B.x,A.y-B.y);
}

int Cross(co Vector&A,co Vector&B)
{
    return A.x*B.y-A.y*B.x;
}

int Dot(co Vector&A,co Vector&B)
{
    return A.x*B.x+A.y*B.y;
}

int Dist2(co Vector&A,co Vector&B)
{
    return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}

vector<Point>ConvexHull(vector<Point>&p)
{
    sort(p.begin(),p.end());
    p.erase(unique(p.begin(),p.end()),p.end());
    
    int n=p.size();
    int m=0;
    vector<Point>ch(n+1);
    for(int i=0;i<n;++i)
    {
        while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)
            --m;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;--i)
    {
        while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)
            --m;
        ch[m++]=p[i];
    }
    if(n>1)
        --m;
    ch.resize(m);
    return ch;
}

int Diameter2(vector<Point>&points)
{
    vector<Point>p=ConvexHull(points);
    int n=p.size();
    if(n==1)
        return 0;
    if(n==2)
        return Dist2(p[0],p[1]);
    p.push_back(p[0]); // avoid %
    int ans=0;
    for(int u=0,v=1;u<n;++u)
    {
        for(;;)
        {
            int diff=Cross(p[u+1]-p[u],p[v+1]-p[v]);
            if(diff<=0)
            {
                ans=max(ans,Dist2(p[u],p[v]));
                if(diff==0)
                    ans=max(ans,Dist2(p[u],p[v+1]));
                break;
            }
            v=(v+1)%n;
        }
    }
    return ans;
}

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int T=read<int>();
    while(T--)
    {
        int n=read<int>();
        vector<Point>points;
        for(int i=0;i<n;++i)
        {
            int x=read<int>(),y=read<int>(),w=read<int>();
            points.push_back(Point(x,y));
            points.push_back(Point(x+w,y));
            points.push_back(Point(x,y+w));
            points.push_back(Point(x+w,y+w));
        }
        printf("%d\n",Diameter2(points));
    }
    return 0;
}