Codeforces gym 102460L Largest Quadrilateral

题目链接

一、题目

N个点坐标分别是(X_i,Y_i),坐标都是整数。

选择4个点作为四边形的顶点,求最大四边形面积。

多组测例。T \leqslant 3

数据范围:4 \leqslant N \leqslant 4096 , 0 \leqslant X_i , Y_i \leqslant 10^9

二、题解

直接上O(n^4)......

这题时限 6s 竟然卡O(n^2logn)的做法我是不理解的。

O(n^2logn)的做法就是枚举对角线然后三分两侧的点,但是过不去,竟然卡这个常数。

接着优化,考虑O(n^2),那就是旋转卡壳了。

先求一下凸包,然后分类讨论。

(1)凸包上只有两个点,答案是0

(2)凸包上只有三个点,那么答案应该是最大的凹四边形的面积,那么枚举凸包内部点,用凸包的三角形减去这些内部点作为顶点的三角形面积,取最大值,具体看代码吧。

(3)凸包上的点多于三个,那么答案显然是凸包上的四个点,那么凸包内部点就不考虑了。枚举对角线,假设对角线的端点是i,j,另外两个端点是l,r,并且 l,r 在对角线的两侧。

因为凸包有单峰性,所以在 i 固定时 l,r 扫一遍就行了,复杂度稳定 O(n^2) 。

因为坐标是整数,所以答案是整数或者小数部分是0.5,所以可以用long\;long运算,减小常数。

三、代码 

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
const int mod = 998244353 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
mt19937  rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ; 
int n ;
struct point
{
    ll x , y ;
    bool operator < (const point &s) const
    {
        if(x != s.x)  return x < s.x ;
        else  return y < s.y ;
    }
} p[maxn] ;
ll multi(point p0 , point p1 , point p2) 
{
    ll x1 , y1 , x2 , y2 ;
    x1 = p1.x - p0.x , y1 = p1.y - p0.y ;
    x2 = p2.x - p0.x , y2 = p2.y - p0.y ;
    return x1 * y2 - x2 * y1 ; 
}
ll area(point p0 , point p1 , point p2)
{
    ll sum = 0 ;
    sum += multi((point){0 , 0} , p0 , p1) ;
    sum += multi((point){0 , 0} , p1 , p2) ;
    sum += multi((point){0 , 0} , p2 , p0) ;
    return abs(sum) ;
}
struct Convex_hull
{
    int cnt , sta[maxn] ;
    void init()
    {
        cnt = 0 ;
    }
    void solve()  //注意左下角的点存储了两次 分别是数组中的第一个点和最后一个点 
    {
        sort(p + 1 , p + n + 1) ;
        sta[++ cnt] = 1 ;
        rep(i , 2 , n) 
        {
            //假如想让凸包的边上有多个点,那就把 <= 改成 < 
            while(cnt >= 2  && multi(p[sta[cnt - 1]] , p[sta[cnt]] , p[i]) <= 0)
            cnt -- ;
            sta[++ cnt] = i ;
        }
        int temp = cnt ;
        per(i , n - 1 , 1)
        {
            while(cnt > temp && multi(p[sta[cnt - 1]] , p[sta[cnt]] , p[i]) <= 0)
            cnt -- ;
            sta[++ cnt] = i ;
        }
        rep(i , 1 , cnt - 1)  sta[i + (cnt - 1)] = sta[i] ;
    }
    void cal()
    {
        int siz = cnt - 1 ;
        ll ans = 0 ;
        if(siz == 2)  cout << "0\n" ;
        else if(siz == 3)
        {
            rep(i , 1 , n)
              if(i != sta[1] && i != sta[2] && i != sta[3])
              {
                ans = max(ans , area(p[sta[1]] , p[sta[2]] , p[sta[3]]) - area(p[sta[1]] , p[sta[2]] , p[i])) ;
                ans = max(ans , area(p[sta[1]] , p[sta[2]] , p[sta[3]]) - area(p[sta[2]] , p[sta[3]] , p[i])) ;
                ans = max(ans , area(p[sta[1]] , p[sta[2]] , p[sta[3]]) - area(p[sta[1]] , p[sta[3]] , p[i])) ;
              }
            if(ans % 2 == 1)  cout << ans / 2 << ".5\n" ;
            else  cout << ans / 2 << '\n' ;
        }
        else
        {
            rep(i , 1 , siz)
            {
                int l = i + 1 , r = i + 3 ;
                rep(j , i + 2 , siz)
                {
                    while(l + 1 < j && area(p[sta[i]] , p[sta[j]] , p[sta[l + 1]]) > area(p[sta[i]] , p[sta[j]] , p[sta[l]]))  l ++ ;
                    while(r + 1 < siz + i && area(p[sta[i]] , p[sta[j]] , p[sta[r + 1]]) > area(p[sta[i]] , p[sta[j]] , p[sta[r]]))  r ++ ;
                    ans = max(ans , area(p[sta[i]] , p[sta[j]] , p[sta[l]]) + area(p[sta[i]] , p[sta[j]] , p[sta[r]])) ;
                }
            }
            if(ans % 2 == 1)  cout << ans / 2 << ".5\n" ;
            else  cout << ans / 2 << '\n' ;
        }
    }
} convex_hull ; 
int main()
{
    ios ;
    int t ;
    cin >> t ;
    while(t --)
    {
        cin >> n ;
        rep(i , 1 , n)  cin >> p[i].x >> p[i].y ;
        convex_hull.init() ;
        convex_hull.solve() ;
        convex_hull.cal() ;
    }
    return 0 ;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值