线段树解决偏序问题

1.E - Buses and People
题意:给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a’,b’,c’),求满足 a<=a’, b’<=b, c’<=c 的最小 c 对应的元组编号。
思路:

  1. 首先肯定离线排序处理,我们按照a排序,那么它之前的元组肯定都满足第一个条件。
  2. 它之前的元组满足b’<=b,最小的c的下标怎么处理呢?题目说每个c都是不同的,我们不妨在c上建立线段树,维护最大值b和下标,查询的时候直接查询大于c的区间。
  3. 所以问题就转换为给定一个区间,求区间内最小的下标满足权值大于某个给定值。这个问题只需要在线段树上优先查询左子区间是否有满足情况的点,向下递归询问即可,注意要用区间max进行剪枝。
  4. 查询的时候记得剪枝,因为线段树维护区间最大值的时候就是为了省时间。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
int n, q, m, pos, x, r, val, cnt = 0, op;
int ans[N], mp[N];
struct node {
    
    

    int l, r, t, id;
    bool operator<(const node &a)const {
    
    
        if(a.l != l)
            return l < a.l;
        return id < a.id;
    }

} a[N];

vector<int>G[N];
struct seg_tree {
    
    ///三维偏序问题
    int id[N * 4], val[N * 4];
    void pushup(int i) {
    
    
        val[i] = max(val[i * 2], val[i * 2 + 1]);
    }
    void build(int i, int l, int r) {
    
    
        id[i] = -1, val[i] = 0;
        if(l == r)
            return ;
        int mid = (l + r) / 2;
        build(i * 2, l, mid);
        build(i * 2 + 1, mid + 1, r);
    }
    void update(int i, int l, int r, int pos, int _val, int _id) {
    
    
        if(l == r) {
    
    
            id[i] = _id;
            val[i] = max(val[i], _val);
            return ;
        }
        int mid = (l + r) / 2;
        if(pos <= mid)
            update(i * 2, l, mid, pos, _val, _id);
        else
            update(i * 2 + 1, mid + 1, r, pos, _val, _id);
        pushup(i);
    }
    int query(int i, int l, int r, int ql, int qr, int _val) {
    
    
        if(ql <= l && qr >= r) {
    
    
            if(val[i] < _val)
                return -1;
        }
        if(l == r) {
    
    
            if(val[i] >= _val)
                return id[i];
            return -1;
        }

        int ans = -1, mid = (l + r) / 2;
        if(ql <= mid) {
    
    
            ans = query(i * 2, l, mid, ql, qr, _val);
            if(ans != -1)
                return ans;
        }
        if(qr > mid) {
    
    
            ans = query(i * 2 + 1, mid + 1, r, ql, qr, _val);
            if(ans != -1)
                return ans;
        }
        return ans;
    }

} seg;

int main() {
    
    

    int cnt = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n + m; i++) {
    
    
        scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].t);
        a[i].id = i;
        mp[++cnt] = a[i].t;
    }
    sort(a + 1, a + n + m + 1);
    sort(mp + 1, mp + cnt + 1);
    cnt = unique(mp + 1, mp + cnt + 1) - mp - 1;
    seg.build(1, 1, cnt);
    for(int i = 1; i <= n + m; i++) {
    
    
        int pos = lower_bound(mp + 1, mp + cnt + 1, a[i].t) - mp;
        if(a[i].id <= n) {
    
    
            seg.update(1, 1, cnt, pos, a[i].r, a[i].id);
        } else
            ans[a[i].id - n] = seg.query(1, 1, cnt, pos, cnt, a[i].r);
    }
    for(int i = 1; i <= m; i++)
        printf("%d ", ans[i]);

}


2.D. Points
题意
给你一个笛卡尔坐标系,现在要支持三种操作,第一种操作是添加一个点(x,y),第二种操作是删除一个点(x,y), 第三种操作是查询严格在点(x,y)右上角的点中,横坐标最小的点,如果有多个点,选择纵坐标最小的那个。

思路
首先题中给出的坐标范围都是1e9,所以需要对x轴进行离散化,建立线段树,每个节点存储区间内所有点中y的最大值,所以叶子节点存储的是当前x下y的最大值。 之后对于每个x开一个set存储横坐标为x的所有y。
之后对于操作1,2,我们只需要单点更新,并且在set中进行insert和erase。
对于操作3,我们首先转换为经典问题,求x+1到inf区间内最小的下标满足权值大于y,我们只要在线段树上优先看左子树是否有满足条件的点,转换为自问题递归求解即可,注意要用区间max进行剪枝,不然复杂度会退化。
这样我们就知道最小的存在大于y的下标x,之后在x所在的set中upper_bound查找第一个大于y的值即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
int n, q, m, pos, x, r, val, cnt = 0, op;
int ans[N],  b[N], c[N];
struct node {
    
    

    char op[10];
    int x, y;

} a[N];
map<int, int>mp;
struct seg_tree {
    
    ///偏序问题
    int id[N * 4], val[N * 4];
    set<int>s[N * 4];
    void pushup(int i) {
    
    
        val[i] = max(val[i * 2], val[i * 2 + 1]);
    }
    void build(int i, int l, int r) {
    
    
        val[i] = 0, id[i] = -1;
        if(l == r) {
    
    
            s[l].insert(-1);
            val[i] = -1;
            return ;
        }
        int mid = (l + r) / 2;
        build(i * 2, l, mid);
        build(i * 2 + 1, mid + 1, r);
    }
    void update(int i, int l, int r, int pos, int _val, int flag) {
    
    
        if(l == r) {
    
    
            if(flag)
                s[l].insert(_val);
            else
                s[l].erase(_val);
            id[i] = pos;
            val[i] = *(s[l].rbegin());
            return ;
        }
        int mid = (l + r) / 2;
        if(pos <= mid)
            update(i * 2, l, mid, pos, _val, flag);
        else
            update(i * 2 + 1, mid + 1, r, pos, _val, flag);
        pushup(i);
    }
    int query(int i, int l, int r, int ql, int qr, int _val) {
    
    
        if(ql>r||qr<l)return -1;
        if(ql <= l && qr >= r) {
    
    
            if(val[i] <= _val)
                return -1;
        }
        if(l == r) {
    
    
            return id[i];
        }
        int mid = (l + r) / 2, ans = -1;
        if(ql <= mid) {
    
    
            ans = query(i * 2, l, mid, ql, qr, _val);
            if(ans != -1)
                return ans;
        }
        if(qr > mid) {
    
    
            ans = query(i * 2 + 1, mid + 1, r, ql, qr, _val);
            if(ans != -1)
                return ans;
        }
        return -1;
    }

} seg;

int main() {
    
    
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
    
    
        scanf("%s%d%d", a[i].op, &a[i].x, &a[i].y);
        b[i] = a[i].x;
    }
    sort(b + 1, b + n + 1);
    int res = unique(b + 1, b + n + 1) - b - 1;
    for(int i = 1; i <= n; i++) {
    
    
        c[i] = lower_bound(b + 1, b + res + 1, a[i].x) - b;
        mp[c[i]] = a[i].x;
    }
    seg.build(1, 1, res);
    for(int i = 1; i <= n; i++) {
    
    
        if(a[i].op[0] == 'a')
            seg.update(1, 1, res, c[i], a[i].y, 1);
        else if(a[i].op[0] == 'r')
            seg.update(1, 1, res, c[i], a[i].y, 0);
        else {
    
    
            int pos = seg.query(1, 1, res, c[i] + 1, res, a[i].y);
            if(pos == -1)
                printf("-1\n");
            else {
    
    
                auto it = seg.s[pos].upper_bound(a[i].y);
                printf("%d %d\n", mp[pos], *it);
            }
        }
    }


}


猜你喜欢

转载自blog.csdn.net/qq_43653111/article/details/105388682