QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: pystraf

Posted at: 2025-12-13 13:47:52

Last updated: 2025-12-13 13:48:55

Back to Problem

Editorial for #4409

Description

给定 $n$ 个互不重合的点 $(x_i,y_i)$,点有颜色 $c_i$。

$q$ 次查询 $(A,B,C)$,求出半平面 $Ax+By+C<0$ 包含的所有点中,选出两个点 $i\le j$,$c_i=c_j$ 的方案数。保证半平面不退化。

Limitations

$1\le n\le 5\times 10^4$

$1\le q\le 5\times 10^5$

$|x_i|,|y_i|,|A|,|B|,|C|\le 10^9$

保证 $x_i,y_i$ 随机。

$8\text{s},512\text{MB}$

Solution

基于随机的做法。

先把点和半平面反过来,那么查询会有 $A<0,A=0,A>0$ 三种情况,分开做就行。

然后答案就是对于每个颜色 $c$,设这个点被颜色为 $c$ 的所有半平面覆盖 $k$ 次,$k^2$ 的和。

这是个半平面数点,先建出 KDT。对每个颜色的半平面,在 KDT 上找出对应的节点打 $\text{tag}$,更新完后统一下放,这样 $\text{tag}$ 间不会互相影响。那么最后对于一个点,答案可以直接加 $\text{tag}^2$。

由于数据随机,复杂度为 $O(n\sqrt q)$,但是 KDT 常数太大在 LG 上过不去。

考虑将 KDT 上面几层先划分出来,然后对每个叶子分别做,这样可以大大减小常数,然后就可以 $5\text{s}$ 左右过了。

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
    if(a < b){ a = b; return true; }
    return false;
}

template<class T>
bool chmin(T &a, const T &b){
    if(a > b){ a = b; return true; }
    return false;
}

namespace Fastio {}
using Fastio::qin;
using Fastio::qout;

constexpr f8 eps = 1e-8;
struct Point {
    int x, y;
    Point() {}
    Point(int x, int y) : x(x), y(y) {}
};

struct Query {
    f8 x, y; int id;
    Query() {}
    Query(f8 x, f8 y, int id) : x(x), y(y), id(id) {}
};

bool cmpx(const Query& a, const Query& b) { return a.x < b.x; } 
bool cmpy(const Query& a, const Query& b) { return a.y < b.y; }

struct Node {
    int son[2], id, vis;
    unsigned t1, mt1, t2, mt2;
    f8 p[2], l[2], r[2];
};

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n, q;
    qin >> n >> q;

    vector<vector<Point>> pts(n);
    for (int i = 0, x, y, c; i < n; i++) {
        qin >> x >> y >> c;
        c--, pts[c].emplace_back(x, y);
    }

    vector<vector<Query>> qry(3);
    for (int i = 0, A, B, C; i < q; i++) {
        qin >> A >> B >> C;
        if (A == 0) qry[2].emplace_back(B, C, i);
        else qry[A < 0].emplace_back(1.0 * B / A, 1.0 * C / A, i);
    }

    for (int i = 0; i < n; i++) {
        sort(pts[i].begin(), pts[i].end(), [&](auto a, auto b) { return a.y < b.y; });
    }

    vector<Node> t;
    auto create = [&]() {
        t.emplace_back();
        return t.size() - 1;
    };

    auto build = [&](auto&& self, vector<Query>& vec, int l, int r, int o) -> int {
        if (l > r) return -1;
        const int mid = (l + r) >> 1, u = create();
        nth_element(vec.begin() + l, vec.begin() + mid, vec.begin() + r + 1, o == 0 ? cmpx : cmpy);
        t[u].id = vec[mid].id, t[u].p[0] = vec[mid].x, t[u].p[1] = vec[mid].y;
        t[u].t2 = t[u].mt2 = t[u].t1 = t[u].mt1 = t[u].vis = 0;
        t[u].son[0] = self(self, vec, l, mid - 1, !o);
        t[u].son[1] = self(self, vec, mid + 1, r, !o);
        t[u].l[0] = t[u].r[0] = t[u].p[0];
        t[u].l[1] = t[u].r[1] = t[u].p[1];
        for (int j = 0; j < 2; j++) {
            if (t[u].son[j] == -1) continue;
            for (int k = 0; k < 2; k++) {
                chmin(t[u].l[k], t[t[u].son[j]].l[k]);
                chmax(t[u].r[k], t[t[u].son[j]].r[k]);
            }
        }
        return u;
    };

    f8 A, B, C; int tp;
    auto f = [&](f8 x, f8 y) { return A * x + B * y + C < -eps; };
    auto modify = [&](auto&& self, int u) -> void {
        if (u == -1) return;
        int cnt = 0;
        if (tp) cnt = f(t[u].l[0], t[u].r[1]) + f(t[u].r[0], t[u].l[1]);
        else cnt = f(t[u].l[0], t[u].l[1]) + f(t[u].r[0], t[u].r[1]);
        if (cnt == 0) return;
        if (cnt == 2) { t[u].t1++; return; }
        t[u].mt1 += f(t[u].p[0], t[u].p[1]);
        t[u].vis = 1;

        self(self, t[u].son[0]);
        self(self, t[u].son[1]);
    };

    auto pushall = [&](auto &&self, int u) -> void {
        if (u == -1) return;
        if (!t[u].vis) {
            t[u].t2 += t[u].t1 * t[u].t1;
            t[u].t1 = 0;
            return;
        }

        t[u].vis = 0;
        if (~t[u].son[0]) t[t[u].son[0]].t1 += t[u].t1;
        if (~t[u].son[1]) t[t[u].son[1]].t1 += t[u].t1;
        t[u].mt1 += t[u].t1;
        t[u].t1 = 0;
        t[u].mt2 += t[u].mt1 * t[u].mt1;
        t[u].mt1 = 0;
        self(self, t[u].son[0]);
        self(self, t[u].son[1]);
    };

    vector<unsigned> ans(q);
    auto answer = [&](auto &&self, int u, unsigned s) -> void {
        if (u == -1) return;
        s += t[u].t2;
        ans[t[u].id] = s + t[u].mt2;
        self(self, t[u].son[0], s);
        self(self, t[u].son[1], s);
    };

    for (int o = 0; o < 3; o++) {
        const int lim = 7;
        vector<vector<Query>> now;
        now.reserve(1 << lim);
        auto divide = [&](auto &&self, int l, int r, int tp, int dep) -> void {
            if (dep == 0) {
                now.emplace_back();
                for (int j = l; j <= r; j++)
                    now.back().push_back(qry[o][j]);
                return;
            }

            const int mid = (l + r) >> 1;
            nth_element(qry[o].begin() + l, qry[o].begin() + mid, qry[o].begin() + r + 1, tp == 0 ? cmpx : cmpy);
            self(self, l, mid, !tp, dep - 1);
            self(self, mid + 1, r, !tp, dep - 1);
        };
        divide(divide, 0, int(qry[o].size()) - 1, 0, lim);

        for (int k = 0; k < now.size(); k++) {
            t.clear();
            int rt = build(build, now[k], 0, int(now[k].size() - 1), 1);
            for (int i = 0; i < n; i++) {
                for (auto j : pts[i]) {
                    if (o == 0) A = j.y, B = 1, C = j.x;
                    else if (o == 1) A = -j.y, B = -1, C = -j.x;
                    else A = j.y, B = 1, C = 0;
                    tp = A * B < 0;
                    modify(modify, rt);
                }
                pushall(pushall, rt);
            }
            answer(answer, rt, 0);
        }
    }

    for (int i = 0; i < q; i++) qout << ans[i] << '\n';

    return 0;
}

Comments

No comments yet.