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;
}