1.E - Buses and People
题意:给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a’,b’,c’),求满足 a<=a’, b’<=b, c’<=c 的最小 c 对应的元组编号。
思路:
- 首先肯定离线排序处理,我们按照a排序,那么它之前的元组肯定都满足第一个条件。
- 它之前的元组满足b’<=b,最小的c的下标怎么处理呢?题目说每个c都是不同的,我们不妨在c上建立线段树,维护最大值b和下标,查询的时候直接查询大于c的区间。
- 所以问题就转换为给定一个区间,求区间内最小的下标满足权值大于某个给定值。这个问题只需要在线段树上优先查询左子区间是否有满足情况的点,向下递归询问即可,注意要用区间max进行剪枝。
- 查询的时候记得剪枝,因为线段树维护区间最大值的时候就是为了省时间。
#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);
}
}
}
}