Submission #70587351


Source Code Expand

#include <bits/stdc++.h>
using namespace std;
using u64 = uint64_t;
using i64 = int64_t;
using i32 = int32_t;
void chmax(auto& a, auto b) { if (a < b) a = b; }
int sz(const auto& a) { return (int)size(a); }
#define rep(i, a, b) for (int i = a; i < (b); i++)

struct P {
    i32 x, y;
    friend auto operator<=>(P a, P b) { return tie(a.x, a.y) <=> tie(b.x, b.y); }
};
const P NIL = {INT_MIN, INT_MIN};
P operator+(P a, P b) { return {a.x + b.x, a.y + b.y}; }
P operator-(P a, P b) { return {a.x - b.x, a.y - b.y}; }
P operator-(P a) { return {-a.x, -a.y}; }
bool operator==(P a, P b) { return a.x == b.x && a.y == b.y; }
i64 dot(P a, P b) { return (i64)a.x * b.x + (i64)a.y * b.y; }
i64 cross(P a, P b) { return (i64)a.x * b.y - (i64)a.y * b.x; }
i64 cross(P a, P b, P c) { return cross(b - a, c - a); }
P rotate(P a) { return {-a.y, a.x}; }
using Poly = vector<P>;

Poly convex_hull(Poly A) {
    ranges::sort(A, less<>{});
    Poly lo, hi;
    for (P p : A) {
        while (lo.size() >= 2 && cross(lo.rbegin()[1], lo.back(), p) <= 0) lo.pop_back();
        while (hi.size() >= 2 && cross(hi.rbegin()[1], hi.back(), p) >= 0) hi.pop_back();
        lo.push_back(p);
        hi.push_back(p);
    }
    if (lo.size() == 1) return lo;
    lo.pop_back();
    lo.insert(lo.end(), hi.rbegin(), hi.rend() - 1);
    return lo;
}

struct T {
    array<P, 4> top4; // vec 方向の上位 4 点
    P nx_vec;         // 子孫のいずれかの top4 が更新されるタイミング
    P nx_vec_8;       // 子の top4 を合わせた 8 点の中の top4 が更新されるタイミング
};
T op(T& a, T& b, const P vec) {
    // dot(vec, *) で比較 (cross(vec, *) で tie-break)
    auto cmp = [&](P a, P b) {
        P v = b - a;
        return pair{dot(vec, v), cross(vec, v)} > pair<i64, i64>{};
    };
    // top4 を cmp でバブルソートしておく (ソート済みなら速い)
    // NIL なら末尾に持っていく
    auto sort_top4 = [&](array<P, 4>& top4) {
        int N = 4, i = 0;
        while (i < N) {
            if (top4[i] == NIL) {
                for (int j = i; j + 1 < N; j++) top4[j] = top4[j + 1];
                top4[N - 1] = NIL;
                N--;
            } else {
                for (int j = i; j--; ) {
                    if (cmp(top4[j], top4[j + 1])) {
                        swap(top4[j], top4[j + 1]);
                    } else break;
                }
                i++;
            }
        }
    };
    sort_top4(a.top4);
    sort_top4(b.top4);
    
    // この頂点が次に更新されるときの方向を求める
    auto upd = [&](P& nx_vec, P v) {
        if (cross(vec, v) <= 0) return;
        if (cross(v, nx_vec) > 0) nx_vec = v;
    };
    
    auto A = a.top4, B = b.top4;
    { // 上 4 点 = A と 下 4 点 = B に分ける
        int i = 0;
        for (; i < 4; i++) {
            P& b = B[i];
            if (b == NIL) break;
            P& a = A[3 - i];
            if (a == NIL || cmp(a, b)) {
                swap(a, b);
            } else break;
        }
    }
    P nx_vec_8 = -vec;
    { // nx_vec_8 := 上 4 点と下 4 点がはじめて逆転する方向 を求める
        rep(j, 0, 4) {
            if (A[j] == NIL) continue;
            rep(k, 0, 4) {
                if (B[k] == NIL) continue;
                upd(nx_vec_8, rotate(A[j] - B[k]));
            }
        }
    }
    P nx_vec = a.nx_vec;
    upd(nx_vec, b.nx_vec);
    upd(nx_vec, nx_vec_8);
    sort_top4(A); // 返り値もソートしておく
    
    return T{A, nx_vec, nx_vec_8};
}
struct SegTree {
    int N;
    P vec;
    vector<T> seg;
    SegTree(P vec, vector<T> A) : N(A.size()), vec(vec), seg(N * 2) {
        rep(i, 0, N) seg[i + N] = A[i];
        for (int i = N; --i; ) seg[i] = op(seg[i << 1], seg[i << 1 | 1], vec);
    }
    P nx_vec() const { return seg[1].nx_vec; }
    auto top4() const { return seg[1].top4; }
    bool rotate(auto F) {
        vec = nx_vec();
        auto dfs = [&](auto dfs, int i) -> bool {
            // nx_vec == vec であるノードをすべて更新
            // top4 が更新されたかどうかを返す
            P v = seg[i].nx_vec;
            if (cross(vec, v) != 0) return false;
            if (dot(vec, v) <= 0) return false;
            if (i >= N) {
                seg[i] = F(i - N);
                return true;
            }
            bool updated = dfs(dfs, i << 1);
            updated |= dfs(dfs, i << 1 | 1);
            // 子の top4 が更新されなかったら、nx_vec のみ更新
            if (!updated && cross(vec, seg[i].nx_vec_8) > 0) {
                auto& nx_vec = seg[i].nx_vec;
                nx_vec = seg[i].nx_vec_8;
                if (cross(seg[i << 1].nx_vec, nx_vec) > 0) nx_vec = seg[i << 1].nx_vec;
                if (cross(seg[i << 1 | 1].nx_vec, nx_vec) > 0) nx_vec = seg[i << 1 | 1].nx_vec;
                return false;
            }
            auto top4_old = seg[i].top4;
            seg[i] = op(seg[i << 1], seg[i << 1 | 1], vec);
            if (i == 1) {
                // i == 1 なら、集合の一致でチェック
                ranges::sort(top4_old, greater<>{});
                ranges::sort(seg[i].top4, greater<>{});
            }
            return top4_old != seg[i].top4;
        };
        return dfs(dfs, 1);
    }
};

void solve() {
    // 入力 + color_of 構築 (20 ms)
    int N;
    cin >> N;
    vector<pair<P, int>> points(N);
    // 座標 → 色の hash_map
    unordered_map color_of(initializer_list<pair<const P, int>>{}, N, [](P a) { return bit_cast<u64>(a); });
    for (auto& [p, c] : points) {
        cin >> p.x >> p.y >> c;
        color_of[p] = c;
    }
    
    // 色ごとに凸包を取る (20 ms)
    ranges::sort(points, {}, &pair<P, int>::second);
    vector<Poly> hulls;
    {
        auto L = points.begin();
        for (auto R = points.begin(); R++ != points.end(); ) {
            if (R != points.end() && R->second == prev(R)->second) continue;
            Poly A;
            A.reserve(R - L);
            for (auto it = L; it != R; it++) {
                A.push_back(it->first);
            }
            hulls.push_back(convex_hull(A));
            L = R;
        }
    }
    
    const int C = sz(hulls);
    if (C < 4) {
        cout << "0\n";
        return;
    }
    
    auto one = [&](const Poly& H, int i, P vec) {
        assert(0 <= i && i < sz(H));
        if (sz(H) == 1) {
            return T{{H[0], NIL, NIL, NIL}, -vec, -vec};
        }
        P u = H[i], v = H[i + 1 == sz(H) ? 0 : i + 1];
        return T{{u, NIL, NIL, NIL}, rotate(u - v), -vec};
    };
    
    // vec を y+ 側で 180 度回したときの top4 (As と Bs 合わせて 440 ms)
    vector<pair<P, array<P, 4>>> As;
    {
        // it[i] : hulls[i] 上の点のうち,dot(seg.vec, *) が最大のもの
        vector<int> it(C);
        rep(i, 0, C) {
            const Poly& H = hulls[i];
            it[i] = ranges::max_element(H, less<>{}) - H.begin();
        }
        const P vec = P{1, 0};
        SegTree seg(vec, [&] {
            vector<T> A(C);
            rep(i, 0, C) A[i] = one(hulls[i], it[i], vec);
            return A;
        }());
        As.emplace_back(seg.vec, seg.top4());
        while (1) {
            if (cross(vec, seg.nx_vec()) <= 0) break;
            if (!seg.rotate([&](int i) {
                const Poly& H = hulls[i];
                int& p = it[i];
                if (++p == sz(H)) p = 0;
                return one(H, p, seg.vec);
            })) continue;  // top4 が同じならスキップ
            As.emplace_back(seg.vec, seg.top4());
        }
    }
    
    // vec を y- 側で 180 度回したときの top4
    vector<pair<P, array<P, 4>>> Bs;
    {
        // it[i] : hulls[i] 上の点のうち,dot(vec, *) が最大のもの
        vector<int> it(C);
        rep(i, 0, C) {
            const Poly& H = hulls[i];
            it[i] = ranges::min_element(H, less<>{}) - H.begin();
        }
        const P vec = P{-1, 0};
        SegTree seg(vec, [&] {
            vector<T> A(C);
            rep(i, 0, C) A[i] = one(hulls[i], it[i], vec);
            return A;
        }());
        Bs.emplace_back(seg.vec, seg.top4());
        while (1) {
            if (cross(vec, seg.nx_vec()) <= 0) break;
            if (!seg.rotate([&](int i) {
                const Poly& H = hulls[i];
                int& p = it[i];
                if (++p == sz(H)) p = 0;
                return one(H, p, seg.vec);
            })) continue;  // top4 が同じならスキップ
            Bs.emplace_back(seg.vec, seg.top4());
        }
    }

    // sz(As) + sz(Bs) ≤ N くらいのはず
    As.push_back(Bs[0]);
    Bs.push_back(As[0]);

    vector<tuple<P, int, int>> cands_AC;
    { 
        // ベクトル AC を dedup するための hash_set
        unordered_set hash_AC(initializer_list<pair<P, P>>{}, N * 16, [](const pair<P, P>& p) {
            u64 h = bit_cast<u64>(p.first) * 0x9e3779b97f4a7c16 + bit_cast<u64>(p.second);
            h ^= h << 13;
            h ^= h >> 7;
            h ^= h << 17;
            return h;
        });
        // 目標方向 (= vec) を回転させながら、点 A, C を top4 から選び、ベクトル AC の候補を列挙 (170 ms)
        auto p = As.begin(), q = Bs.begin();
        while (p->first != P{-1, 0}) {
            for (P a : p->second) {
                for (P c : q->second) {
                    int c1 = color_of[a], c3 = color_of[c];
                    if (c1 == c3) continue;
                    if (c1 > c3) swap(c1, c3);
                    if (!hash_AC.insert(minmax({a, c})).second) continue;
                    P v = rotate(a - c); // 90 度回転させておき,B-D の目標ベクトルにする
                    assert(v != P{});
                    // y+ 方向に正規化
                    if (P{v.y, v.x} < P{}) v = -v; 
                    cands_AC.emplace_back(v, c1, c3);
                }
            }
            const i64 c = cross(next(p)->first, -next(q)->first);
            if (c >= 0) p++;
            if (c <= 0) q++;
        }
    }
    // sz(cands_AC) ≤ 4N くらいのはず

    { // sort cands_AC (10 ms)
        ranges::sort(cands_AC, [](const auto& a, const auto& b) {
            return cross(get<0>(a), get<0>(b)) > 0;
        });
    }

    i64 ans = 0;
    { // AC を回して、点 B, D を目標方向の top4 から選び、四角形を列挙 (80 ms)
        auto p = As.begin(), q = Bs.begin();
        for (auto [vec, c1, c3] : cands_AC) {
            while (cross(next(p)->first, vec) > 0) p++;
            while (cross(-next(q)->first, vec) > 0) q++;
            for (P b : p->second) {
                const int c2 = color_of[b];
                if (c2 == c1 || c2 == c3) continue;
                for (P d : q->second) {
                    const int c4 = color_of[d];
                    if (c4 == c1 || c4 == c2 || c4 == c3) continue;
                    chmax(ans, dot(vec, b - d));
                }
            }
        }
    }
    cout << ans << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    rep(_, 0, T) solve();
}

Submission Info

Submission Time
Task L - Colorful Quadrilateral
User tatyam
Language C++ 23 (gcc 12.2)
Score 100
Code Size 11499 Byte
Status AC
Exec Time 702 ms
Memory 57844 KiB

Judge Result

Set Name Sample All
Score / Max Score 0 / 0 100 / 100
Status
AC × 1
AC × 60
Set Name Test Cases
Sample 00-sample-001.txt
All 00-sample-001.txt, 01-corner-001.txt, 02-10x10000-001.txt, 02-10x10000-002.txt, 03-100x1000-001.txt, 03-100x1000-002.txt, 04-sqrtxsqrt-001.txt, 04-sqrtxsqrt-002.txt, 05-1000x100-001.txt, 05-1000x100-002.txt, 06-5000x20-001.txt, 06-5000x20-002.txt, 06-5000x20-003.txt, 06-5000x20-004.txt, 06-5000x20-005.txt, 06-5000x20-006.txt, 06-5000x20-007.txt, 06-5000x20-008.txt, 06-5000x20-009.txt, 06-5000x20-010.txt, 07-convex1-002.txt, 07-convex1-008.txt, 07-convex1-012.txt, 08-convex2-004.txt, 08-convex2-014.txt, 08-convex2-015.txt, 08-convex2-016.txt, 08-convex2-017.txt, 08-convex2-018.txt, 08-convex2-019.txt, 09-convex3-001.txt, 09-convex3-002.txt, 09-convex3-012.txt, 10-convex4-002.txt, 10-convex4-003.txt, 10-convex4-012.txt, 10-convex4-013.txt, 10-convex4-014.txt, 11-manylayer-009.txt, 11-manylayer-010.txt, 12-biaslayer-004.txt, 12-biaslayer-007.txt, 12-biaslayer-010.txt, 12-biaslayer-019.txt, 13-anstriangle-001.txt, 14-ansboomerang-002.txt, 15-colinear-002.txt, 16-handmade-random-006.txt, 16-handmade-random-007.txt, 18-4x9999_60004x1-004.txt, 19-100x500_50000x1-003.txt, 20-rndsmall-001.txt, 20-rndsmall-006.txt, 21-rndmiddle-001.txt, 23-verify_deltion-001.txt, 23-verify_deltion-002.txt, 23-verify_deltion-003.txt, 24-verify_recolor-001.txt, 24-verify_recolor-002.txt, 24-verify_recolor-003.txt
Case Name Status Exec Time Memory
00-sample-001.txt AC 4 ms 3312 KiB
01-corner-001.txt AC 1 ms 3396 KiB
02-10x10000-001.txt AC 177 ms 3440 KiB
02-10x10000-002.txt AC 260 ms 3416 KiB
03-100x1000-001.txt AC 185 ms 3544 KiB
03-100x1000-002.txt AC 241 ms 3596 KiB
04-sqrtxsqrt-001.txt AC 176 ms 3732 KiB
04-sqrtxsqrt-002.txt AC 271 ms 3580 KiB
05-1000x100-001.txt AC 196 ms 4072 KiB
05-1000x100-002.txt AC 187 ms 4036 KiB
06-5000x20-001.txt AC 264 ms 6604 KiB
06-5000x20-002.txt AC 79 ms 5876 KiB
06-5000x20-003.txt AC 212 ms 6540 KiB
06-5000x20-004.txt AC 213 ms 6224 KiB
06-5000x20-005.txt AC 245 ms 6544 KiB
06-5000x20-006.txt AC 198 ms 6020 KiB
06-5000x20-007.txt AC 114 ms 5848 KiB
06-5000x20-008.txt AC 246 ms 6200 KiB
06-5000x20-009.txt AC 187 ms 5800 KiB
06-5000x20-010.txt AC 245 ms 6500 KiB
07-convex1-002.txt AC 403 ms 54788 KiB
07-convex1-008.txt AC 702 ms 52772 KiB
07-convex1-012.txt AC 461 ms 57144 KiB
08-convex2-004.txt AC 316 ms 47224 KiB
08-convex2-014.txt AC 386 ms 50840 KiB
08-convex2-015.txt AC 253 ms 39488 KiB
08-convex2-016.txt AC 280 ms 42664 KiB
08-convex2-017.txt AC 265 ms 42776 KiB
08-convex2-018.txt AC 350 ms 48644 KiB
08-convex2-019.txt AC 307 ms 43572 KiB
09-convex3-001.txt AC 378 ms 50372 KiB
09-convex3-002.txt AC 300 ms 47024 KiB
09-convex3-012.txt AC 227 ms 41268 KiB
10-convex4-002.txt AC 379 ms 50172 KiB
10-convex4-003.txt AC 282 ms 42260 KiB
10-convex4-012.txt AC 237 ms 47832 KiB
10-convex4-013.txt AC 311 ms 47976 KiB
10-convex4-014.txt AC 289 ms 48300 KiB
11-manylayer-009.txt AC 84 ms 24008 KiB
11-manylayer-010.txt AC 41 ms 23304 KiB
12-biaslayer-004.txt AC 474 ms 57844 KiB
12-biaslayer-007.txt AC 694 ms 52772 KiB
12-biaslayer-010.txt AC 473 ms 57704 KiB
12-biaslayer-019.txt AC 279 ms 48092 KiB
13-anstriangle-001.txt AC 56 ms 32812 KiB
14-ansboomerang-002.txt AC 378 ms 48548 KiB
15-colinear-002.txt AC 68 ms 29704 KiB
16-handmade-random-006.txt AC 97 ms 24084 KiB
16-handmade-random-007.txt AC 42 ms 23292 KiB
18-4x9999_60004x1-004.txt AC 416 ms 32704 KiB
19-100x500_50000x1-003.txt AC 208 ms 25596 KiB
20-rndsmall-001.txt AC 175 ms 3448 KiB
20-rndsmall-006.txt AC 161 ms 3524 KiB
21-rndmiddle-001.txt AC 123 ms 3776 KiB
23-verify_deltion-001.txt AC 171 ms 3664 KiB
23-verify_deltion-002.txt AC 422 ms 3720 KiB
23-verify_deltion-003.txt AC 610 ms 9496 KiB
24-verify_recolor-001.txt AC 187 ms 3632 KiB
24-verify_recolor-002.txt AC 282 ms 3944 KiB
24-verify_recolor-003.txt AC 337 ms 9316 KiB