1. 程式人生 > 實用技巧 >2020杭電多校6,HDU- 6830- Asteroid in Love (幾何,凸包)

2020杭電多校6,HDU- 6830- Asteroid in Love (幾何,凸包)

2020杭電多校6,HDU- 6830- Asteroid in Love (幾何,凸包)

Problem Description

During the summer vacation, the geoscience community launched a constellation observation activity.

On a clear night, Mira and Ao set up their telescopes. Through the telescopes, they observed n stars. These n stars could be regarded as points on a two-dimensional plane, with coordinates (xi,yi).

It was boring to observe the stars alone, so they classified these stars into three categories according to their spectrum: R-type stars, G-type stars, and B-type stars.

Now, Mira wants to form a constellation named after Ao, for Ao to make up for the regret that there is no star named after Ao. This constellation should be small and beautiful. She wants the number of stars to be the least in this constellation. Besides, she wants the constellation contains all types of stars. It is easy to know that this constellation has only three stars, and the types of these three stars are R type, G type, and B type.

She also wants the area of the convex polygon enclosed by the stars of this constellation is the largest, that is, the area of the triangle formed by these three stars is the largest. If the triangle is trivial (that is, the three points that make up the triangle are on the same straight line), the area of the triangle is considered to be 0.

Please tell Mira the largest area of Constellation Ao.

題意:

給定\(\mathit n\) 個分為\(\text 3\)類的二維平面上的點,每一類至少有一個點,現在讓你從三類中各選擇出一個節點,使其三個節點構成的三角形面積最大。求最大的面積值,其中\(n\leq 3\times 10^3\)

思路:

很容易想到的是列舉兩個類中的節點,然後再第三個類中找一個節點,使其與前兩點構成的直線距離最遠。

假設我們列舉的是分別屬於類\(0,1\)中的兩個節點\(A,B\)那麼屬於類\(\text 2\) 的點\(\mathit C\)一定屬於類\(\text 2\) 中所有點構成的凸包中。

因為若不在凸包中,一定可以有一個在凸包中的點使其與直線\((A,B)\)距離更遠。

所以現在問題轉化為了,在一個凸包中找到一個點與給定直線距離最大,我們來觀察下圖:

我們用Andrew演算法求凸包的適合,將其分成兩部分,即上輪廓和下輪廓。

通過分析我們可以發現,無論列舉的線段和凸包的位置關係是什麼,上輪廓和下輪廓中的點到線段的距離都是一個單峰函式。

例如上圖中,點\(H,G,F,E,D\)(下輪廓)和線段\((K,L)\)是開口向上的單峰函式。

\(A,B,C,D\)(上輪廓)和線段\((K,L)\)是開口向下的單峰函式。

而對於線段\((I,J)\)開口恰好相反,我們又不方便確定距離函式的開口方向,那麼我們不妨將2種情況都考慮進去(即凸函式和凹函式),分別求出它們的極值,答案一定會被考慮進去。

對於一個單峰函式求極值問題,我們知道三分演算法可以輕易的解決。

注意:一定要把凸包分成兩個凹凸相反的部分,這樣才能保證是單峰函式。

程式碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <bits/stdc++.h>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x)  if(DEBUG_Switch) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
void pvarr_int(int *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%d%c", arr[i], i == n ? '\n' : ' ');}}
void pvarr_LL(ll *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%lld%c", arr[i], i == n ? '\n' : ' ');}}
const int maxn = 3010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
#define DEBUG_Switch 0
const double pi = acos(-1);

struct Point
{
    ll x, y;
    Point() {}
    Point(ll _x, ll _y)
    {
        x = _x; y = _y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x, y - b.y);
    }
    //??
    ll operator ^(const Point &b)const
    {
        return x * b.y - y * b.x;
    }
};
Point base;
inline bool cmp(const Point& a, const  Point& b)
{
    if (a.x != b.x)
        return a.x < b.x;
    else
        return a.y < b.y;
    // double A = atan2((a.y - base.y), (a.x - base.x));
    // double B = atan2((b.y - base.y), (b.x - base.x));
    // if (A != B)return A < B;
    // else    return a.x < b.x;
}
long long Cross(Point a, Point b, Point c)
{
    return 1LL * (b.x - a.x) * (c.y - a.y) - 1LL * (b.y - a.y) * (c.x - a.x);
}
void Get(int n, Point *p , Point * S, int &top, int op)
{
    sort(&p[1], &p[n + 1], cmp);
    top = 0;
    if (op)
        for (int i = 1; i <= n;)
        {
            while (top > 1 && Cross(S[top - 1], p[i], S[top]) >= 0)
                top--;
            S[++top] = p[i++];
        }
    else
        for (int i = n; i >= 1;)
        {
            while (top > 1 && Cross(S[top - 1], p[i], S[top]) >= 0)
                top--;
            S[++top] = p[i--];
        }
}
int n;
Point p[3][maxn];
int cnt[3];
Point S1[maxn], S2[maxn];
int top1, top2;
ll solve(Point a, Point b)
{
    ll h = 0ll;
    int l, r;
    l = 1;
    r = top1;
    while (r - l >= 3)
    {
        int lmid = (l + l + r) / 3;
        int rmid = (l + r + r) / 3;
        if (abs((a - b) ^ (a - S1[lmid])) > abs((a - b) ^ (a - S1[rmid])) )
        {
            r = rmid;
        } else
        {
            l = lmid;
        }
    }
    repd(i, l, r)
    {
        h = max(h, abs((a - b) ^ (a - S1[i])));
    }
    l = 1;
    r = top1;
    while (r - l >= 3)
    {
        int lmid = (l + l + r) / 3;
        int rmid = (l + r + r) / 3;
        if (abs((a - b) ^ (a - S1[lmid])) < abs((a - b) ^ (a - S1[rmid])) )
        {
            r = rmid;
        } else
        {
            l = lmid;
        }
    }
    repd(i, l, r)
    {
        h = max(h, abs((a - b) ^ (a - S1[i])));
    }
    l = 1;
    r = top2;
    while (r - l >= 3)
    {
        int lmid = (l + l + r) / 3;
        int rmid = (l + r + r) / 3;
        if (abs((a - b) ^ (a - S2[lmid])) > abs((a - b) ^ (a - S2[rmid])) )
        {
            r = rmid;
        } else
        {
            l = lmid;
        }
    }
    repd(i, l, r)
    {
        h = max(h, abs((a - b) ^ (a - S2[i])));
    }
    l = 1;
    r = top2;
    while (r - l >= 3)
    {
        int lmid = (l + l + r) / 3;
        int rmid = (l + r + r) / 3;
        if (abs((a - b) ^ (a - S2[lmid])) < abs((a - b) ^ (a - S2[rmid])) )
        {
            r = rmid;
        } else
        {
            l = lmid;
        }
    }
    repd(i, l, r)
    {
        h = max(h, abs((a - b) ^ (a - S2[i])));
    }
    return h;
}
int main()
{
#if DEBUG_Switch
    freopen("C:\\code\\input.txt", "r", stdin);
#endif
    //freopen("C:\\code\\output.txt","w",stdout);
    int t;
    t = readint();
    while (t--)
    {
        cnt[0] = cnt[1] = cnt[2] = 0;
        n = readint();
        Point temp;
        repd(i, 1, n)
        {
            temp.x = readint();
            temp.y = readint();
            int tp = readint();
            cnt[tp]++;
            p[tp][cnt[tp]] = temp;
        }
        Get(cnt[0], p[0], S1, top1, 1);
        Get(cnt[0], p[0], S2, top2, 0);
        ll ans = 0ll;
        repd(i, 1, cnt[1])
        {
            repd(j, 1, cnt[2])
            {
                ans = max(ans, solve(p[1][i], p[2][j]));
            }
        }
        if (ans & 1)
        {
            printf("%lld.5\n", ans / 2 );
        } else
        {
            printf("%lld.0\n", ans / 2 );
        }
    }

    return 0;
}