CF - 19D【线段树】

D. Points

  • 题意:在笛卡尔坐标系上有 n 个操作,添加点 (x, y),删除点 (x, y) 和查询点 (x, y) 的最近的右上角的点 (x1, y1) 【在保证(x1 - x)最小的基础上寻找(y1-y)最小】

思路1:横纵坐标的范围很大,所以我们对横纵坐标离散化,然后对离散后的横坐标用线段树维护,线段树维护横坐标 x 对应竖直线上点的个数。我们用 set 存储横坐标 x 对应竖直线上点的集合。点的添加和删除操作直接跑到叶子节点对 set 进行insert或者erase即可。查询点 (x, y) 操作的时候我们可以通过查询线段树找到横坐标大于 x 并且 x 对应集合中有纵坐标大于 y 的最小的 x 。找到x之后我们就从x的集合中找到第一个纵坐标大于y的点即可。

我们线段树维护区间最大的纵坐标点值,当查询时判断区间中是否有大于y的纵坐标点。如果左区间有可行点,那么一定比右区间的可行点优。当左区间没有可行点时再考虑右区间的可行点。这样可以保证我们最多只跑两个叶子节点,控制复杂度。

线段树 + set维护纵坐标点
  • 分析
    单次add/remove: O(2logn)//叶子节点一个log + set的插入或删除一个log
    单次find:O(2logn)+O(logn)//最多跑两个叶子节点->两个log + set二分找纵坐标一个log
    总复杂度:O(3nlogn)

不长记性又写cout,TTTTTTTTT!!!

#include <bits/stdc++.h>

#define MID (l + r) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr

using namespace std;
typedef long long ll;
const int maxN = 200005;
int read() {
    
    
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {
    
     if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
int n;
int discx[maxN], discy[maxN];
struct OP {
    
    
    string op;
    int x, y;
}_op[maxN];
int tree[maxN << 2], maxy[maxN << 2]; //维护区间y最大值
set<int>st[maxN]; //st[i]: 纵轴x = i上点的集合
enum lalala{
    
    add, remo};
void pushup(int rt) {
    
    
    tree[rt] = tree[lsn] + tree[rsn];
    maxy[rt] = max(maxy[lsn], maxy[rsn]);
}
void update(int rt, int l, int r, int xx, int yy, lalala ar) {
    
     //纵轴x=xx上点的个数加1,点增加一个yy
    if(l == r) {
    
    
        switch (ar) {
    
    
            case add: {
    
    
                ++ tree[rt], st[l].insert(yy);
                maxy[rt] = *st[l].rbegin();
                break;
            }
            case remo: {
    
    
                -- tree[rt], st[l].erase(yy);
                if(st[l].empty()) maxy[rt] = 0;
                else maxy[rt] = *st[l].rbegin();
                break;
            }
        }
        return ;
    }
    int mid = MID;
    if(xx <= mid) update(Lson, xx, yy, ar);
    else update(Rson, xx, yy, ar);
    pushup(rt);
}
int query(int rt, int l, int r, int xx, int yy) {
    
     //找到[l, r]中第一个大于xx的并且存在纵坐标点大于yy的集合
    if(!tree[rt] || maxy[rt] <= yy) {
    
    
        return -1;
    }
    if(l == r) {
    
    
        if(l <= xx) return -1;
        return l;
    }
    int mid = MID;
    if(mid > xx) {
    
    
        int lans = query(Lson, xx, yy);
        if(lans == -1) return query(Rson, xx, yy);
        return lans;
    } else {
    
    
        return query(Rson, xx, yy);
    }
}
int main() {
    
    
    n = read();
    for(int i = 0; i < n; ++ i ) {
    
    
        string op; cin >> op;
        int xx = discx[i] = read();
        int yy = discy[i] = read();
        _op[i] = OP{
    
    op, xx, yy};
    }
    sort(discx, discx + n);
    int UPX = unique(discx, discx + n) - discx; //离散化后不同x的个数
    sort(discy, discy + n);
    int UPY = unique(discy, discy + n) - discy; //离散化后不同y的个数
    //更新坐标为离散化后的坐标
    for(int i = 0; i < n; ++ i ) {
    
    
        _op[i].x = lower_bound(discx, discx + UPX, _op[i].x) - discx;
        _op[i].y = lower_bound(discy, discy + UPY, _op[i].y) - discy;
    }
    for(int i = 0; i < n; ++ i ) {
    
    
        switch (_op[i].op[0]) {
    
    
            case 'a': update(1, 0, UPX - 1, _op[i].x, _op[i].y, add); break;
            case 'r': update(1, 0, UPX - 1, _op[i].x, _op[i].y, remo); break;
            case 'f': {
    
    
                int xx = _op[i].x, yy = _op[i].y;
                xx = query(1, 0, UPX - 1, xx, yy);
                if(xx == -1) {
    
     printf("-1\n"); /*fflush(stdout);*/ continue; }
                yy = *st[xx].upper_bound(_op[i].y);
                printf("%d %d\n", discx[xx], discy[yy]);
//                fflush(stdout);
                break;
            }
        }
    }
    return 0;
}

思路2:我们对x*(1e9)+y的离散化的值建立线段树,更新点直接更新x*(1e9)+y的离散化值即可。查询点的时候,我们先二分找到(x+1)*(1e9)的点,也就是找到了可能可行的最小的横坐标ansx。然后我们再找到ansx到离散化的右边界中合法的点。

思路就是这么个思路,就是实现起来有点绕,也可能是理解不深刻,反正写了很久还调了很久,对于各个变量和参数的意义和调用都需要想清楚。

分析:
单次更新点:O(logn)
单次查询:O(logn + 2logn) //二分找最小x,线段树最多两个叶子节点找合法x, y
总复杂度:O(3nlogn)
但是这个做法比第一种快,就快到了更新点。

#include <bits/stdc++.h>

#define MID (l + r) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr

using namespace std;
typedef long long ll;
const int maxN = 200005;
const ll c = 1e9;
int read() {
    
    
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {
    
     if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
int n;
struct node{
    
    
    char op; ll x, y; int xy;
}mem[maxN];
ll disc[maxN], UP;
int tree[maxN << 2]; //离散化后的xc+y
int maxY[maxN << 2]; //原y
enum OP{
    
    add, remo};
void pushup(int rt) {
    
    
    tree[rt] = tree[lsn] + tree[rsn];
    maxY[rt] = max(maxY[lsn], maxY[rsn]);
}
void update(int rt, int l, int r, int x, int y, OP op) {
    
    
    if(l == r) {
    
    
        switch (op) {
    
    
            case add: ++ tree[rt]; maxY[rt] = y; break;
            case remo: -- tree[rt]; maxY[rt] = 0; break;
        }
        return ;
    }
    int mid = MID;
    if(mid < x) update(Rson, x, y, op);
    else update(Lson, x, y, op);
    pushup(rt);
}
pair<int, int> query(int rt, int l, int r, int ql, int qr, int yy) {
    
    
    if(!tree[rt] || maxY[rt] <= yy) return make_pair(-1, -1);
    if(l == r) {
    
    
        if(maxY[rt] <= yy) return make_pair(-1, -1);
        return make_pair(disc[l] / c, maxY[rt]);
    }
    int mid = MID;
    if(ql > mid) return query(QR, yy);
    else if(qr <= mid) return query(QL, yy);
    else {
    
    
        pair<int, int> lans = query(QL, yy);
        if(lans == make_pair(-1, -1)) return query(QR, yy);
        else return lans;
    }
}
int main() {
    
    
    n = read();
    for(int i = 0; i < n; ++ i ) {
    
    
        string op; cin >> op;
        ll x = read(), y = read();
        mem[i] = {
    
    op[0], x, y, 0};
        disc[i] = x * c + y;
    }
    sort(disc, disc + n);
    UP = unique(disc, disc + n) - disc;
    for(int i = 0; i < n; ++ i ) {
    
    
        mem[i].xy = lower_bound(disc, disc + UP, mem[i].x * c + mem[i].y) - disc;
    }
    for(int i = 0; i < n; ++ i ) {
    
    
        switch (mem[i].op) {
    
    
            case 'a': update(1, 0, UP - 1, mem[i].xy, mem[i].y, add); break;
            case 'r': update(1, 0, UP - 1, mem[i].xy, mem[i].y, remo); break;
            case 'f':
                int xx = lower_bound(disc, disc + UP, (mem[i].x + 1) * c) - disc;
                if(xx == UP) {
    
     printf("-1\n"); /*fflush(stdout);*/ continue; }
                pair<int, int >ans = query(1, 0, UP - 1, xx, UP - 1, mem[i].y);
                if(ans == make_pair(-1, -1)) {
    
     printf("-1\n"); /*fflush(stdout);*/ continue; }
                printf("%d %d\n", ans.first, ans.second);
//                fflush(stdout);
                break;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/108807184