Points

Luogu 无法提交 Codeforces 原题,解析已迁移至 codeforces/19D,本页仅保留入口。

OJ: luogu

题目 ID: CF19D

难度:提高+/省选-

标签:线段树树状数组坐标压缩二维查询

日期: 2026-07-16 23:59

题意

动态加入、删除点;查询严格右上方的点,要求先取最小 x,再取该 x 下最小 y。完整教学解析(含 Python 版本与思考过程)已迁移至:

思路

离线坐标压缩:每个 x 组用 Fenwick 维护活跃 y,外层线段树存每组最大 y。查询时先在线段树找第一个 x 更大且最大 y 更大的组,再在组内 Fenwick 找第一个大于 query_y 的活跃 ykth 二进制提升)。

代码

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-08-02
 * update_at: 2026-08-02
 */

/* CF19D Points */
/* 离线坐标压缩:每个 x 组用 Fenwick 维护活跃 y,外层线段树存每组最大 y。
 * find 时先在线段树找第一个 x 更大且最大 y 更大的组,
 * 再在该组 Fenwick 里找第一个大于 query_y 的活跃 y。 */

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

const int MAXN = 200000 + 5;

int n;                         // 操作数
char op[MAXN];
long long ox[MAXN], oy[MAXN];  // 操作参数
vector<long long> xs;          // 所有出现过的 x(升序去重),下标即组号
vector<long long> ys;          // 每个组的 y 连续存放
vector<int> yoff;              // yoff[g]..yoff[g+1] 是组 g 的 y 区间
vector<long long> fenwick;     // 所有组的 Fenwick 共享一块数组
vector<int> bit_off;           // 组 g 的 Fenwick 起点
vector<int> cnt;               // 组 g 当前活跃点数
vector<long long> seg;         // 线段树:组 g 当前最大 y(空组为 -1)
int groups, seg_size;

int bit_len(int g) {
    return yoff[g + 1] - yoff[g];
}

void bit_add(int g, int idx, int delta) {
    int base = bit_off[g];
    int len = bit_len(g);
    while (idx <= len) {
        fenwick[base + idx] += delta;
        idx += idx & -idx;
    }
}

long long bit_sum(int g, int idx) {
    int base = bit_off[g];
    long long res = 0;
    while (idx > 0) {
        res += fenwick[base + idx];
        idx -= idx & -idx;
    }
    return res;
}

// 组内找第 target 个活跃 y 的压缩下标(Fenwick 二进制提升)
int bit_kth(int g, long long target) {
    int base = bit_off[g];
    int len = bit_len(g);
    int idx = 0;
    int step = 1 << (31 - __builtin_clz(len)); // 不超过 len 的最大 2 的幂
    while (step) {
        int nxt = idx + step;
        if (nxt <= len && fenwick[base + nxt] < target) {
            idx = nxt;
            target -= fenwick[base + nxt];
        }
        step >>= 1;
    }
    return idx + 1;
}

void seg_set(int idx, long long value) {
    int node = seg_size + idx;
    seg[node] = value;
    for (node >>= 1; node; node >>= 1)
        seg[node] = max(seg[node << 1], seg[node << 1 | 1]);
}

// 找下标 >= start 且最大 y > y 的最小组号;没有则 -1
int first_x(int node, int l, int r, int start, long long y) {
    if (r <= start || seg[node] <= y)
        return -1;
    if (r - l == 1)
        return l;
    int mid = (l + r) >> 1;
    int res = first_x(node << 1, l, mid, start, y);
    if (res != -1)
        return res;
    return first_x(node << 1 | 1, mid, r, start, y);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    set<pair<long long, long long>> points;
    for (int i = 0; i < n; i++) {
        string s;
        cin >> s >> ox[i] >> oy[i];
        op[i] = s[0];
        if (op[i] == 'a')
            points.insert({ox[i], oy[i]});
    }

    // 坐标压缩:x 升序去重,每个 x 的 y 升序去重
    for (auto &p : points) {
        if (xs.empty() || xs.back() != p.first) {
            xs.push_back(p.first);
            yoff.push_back((int)ys.size());
        }
        ys.push_back(p.second);
    }
    yoff.push_back((int)ys.size());
    groups = (int)xs.size();

    bit_off.assign(groups + 1, 0);
    for (int g = 0; g < groups; g++)
        bit_off[g + 1] = bit_off[g] + bit_len(g) + 1;
    fenwick.assign(bit_off[groups], 0);
    cnt.assign(groups, 0);

    seg_size = 1;
    while (seg_size < groups)
        seg_size <<= 1;
    seg.assign(2 * seg_size, -1);

    for (int i = 0; i < n; i++) {
        if (op[i] != 'f') {
            int g = (int)(lower_bound(xs.begin(), xs.end(), ox[i]) - xs.begin());
            int s = yoff[g], e = yoff[g + 1];
            int pos = (int)(lower_bound(ys.begin() + s, ys.begin() + e, oy[i]) - ys.begin()) - s + 1;
            int delta = (op[i] == 'a') ? 1 : -1;
            bit_add(g, pos, delta);
            cnt[g] += delta;
            long long maxy = -1;
            if (cnt[g] > 0)
                maxy = ys[s + bit_kth(g, cnt[g]) - 1]; // 第 cnt 个即最大
            seg_set(g, maxy);
            continue;
        }

        // find:先找第一个 x 更大的可行组
        int start = (int)(upper_bound(xs.begin(), xs.end(), ox[i]) - xs.begin());
        int g = first_x(1, 0, seg_size, start, oy[i]);
        if (g == -1 || g >= groups) {
            cout << "-1\n";
            continue;
        }
        int s = yoff[g], e = yoff[g + 1];
        int pos = (int)(upper_bound(ys.begin() + s, ys.begin() + e, oy[i]) - ys.begin()) - s;
        long long target = bit_sum(g, pos) + 1; // 第一个严格大于 query_y 的活跃 y 的秩
        long long ans_y = ys[s + bit_kth(g, target) - 1];
        cout << xs[g] << ' ' << ans_y << '\n';
    }
    return 0;
}

复杂度

外层查找、组内 Fenwick 和坐标二分均为 O(log n),所以每个操作 O(log n),空间 O(n)

总结

完整解析已迁移至 codeforces-19D Points