洛谷题单指南-线段树-Points
原题链接:https://www.luogu.com.cn/problem/CF19D
题意解读:坐标系支持集中操作:1.添加一个点(x,y),保证不会重复 2.删除一个点(x,y) 3.查询刚好比一个点(x,y)的x,y都大的点,优先看刚好比x大的位置,如果该位置有多个点,取y最小的一个,找到则输出点的坐标,找不到则输出-1。
解题思路:
首先,可以将所有x对应的y都存下来,由于需要支持添加、删除、找比某个数大的第一个数,可以想到平衡树,set即可支持这些操作。
set<int> xys[N]; //xys[i]存x坐标是i的点对应的所有y坐标
由于x坐标的取值范围较大,可能导致数组爆内存,因此要首先对x坐标进行离散化处理,离散化后最大的x记为maxx。
其次,要实现查询操作find x y,
第一步、可以找到第一个比x大的有合适点的横坐标x',什么叫有合适点?在区间x+1 ~ maxx范围内,找到第一个有最大纵坐标大于y的位置。
既然是区间查询,不难想到可以用线段树来维护所有点x坐标区间最大的y值,定义为
struct Node
{
int l, r;
int maxy; //区间[l,r]范围内的点的最大y值
} tr[N * 4];
这样以来,通过在线段树中查询区间xleft ~ N范围内,第一个maxy > y的叶子结点的位置即找到了上述中的x'。
第二步、通过在xys[x']中二分找到第一个比y大的值,即可得到刚好比x,y都大的点坐标。
最后,在添加、删除点的过程中,要同时维护线段树以及xys,可以先更新xys,再更新线段树。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct Action
{
string op;
int x, y;
} acts[N]; //保存所有的操作
struct Node
{
int l, r;
int maxy; //区间[l,r]范围内的点的最大y值
} tr[N * 4]; //对横坐标范围建立线段树
set<int> xys[N]; //xys[i]存x坐标是i的点对应的所有y坐标
vector<int> allx; //所有横坐标,用于离散化
int n;
void pushup(int u)
{
tr[u].maxy = max(tr[u << 1].maxy, tr[u << 1 | 1].maxy);
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r) tr[u].maxy = -1; //设置默认值
else
{
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
//不需要pushup
}
}
void update(int u, int pos, int val)
{
if(tr[u].l == tr[u].r) tr[u].maxy = val;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(pos <= mid) update(u << 1, pos, val);
else update(u << 1 | 1, pos, val);
pushup(u);
}
}
//在[l,r]区间查找第一个maxy > val的叶子节点的位置
int query(int u, int l, int r, int val)
{
//在[l,r]区间找到一个叶子结点满足要求
if(tr[u].l == tr[u].r && tr[u].l >= l && tr[u].r <= r && tr[u].maxy > val) return tr[u].l;
//该结点maxy不满足要求,或者区间无交
if(tr[u].l > r || tr[u].r < l || tr[u].maxy <= val) return -1;
int left = query(u << 1, l, r, val);
if(left != -1) return left; //先看左子节点是否满足要求
else return query(u << 1 | 1, l, r, val); //再看右子节点
}
int main()
{
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> acts[i].op >> acts[i].x >> acts[i].y; //保存所有操作
allx.push_back(acts[i].x); //提取横坐标
}
sort(allx.begin(), allx.end()); //排序
allx.erase(unique(allx.begin(), allx.end()), allx.end()); //去重
int maxx = allx.size(); //离散化后最大的横坐标
build(1, 1, maxx); //构建线段树
for(int i = 1; i <= n; i++)
{
//用离散化后的值替换横坐标
acts[i].x = lower_bound(allx.begin(), allx.end(), acts[i].x) - allx.begin() + 1;
if(acts[i].op == "add")
{
xys[acts[i].x].insert(acts[i].y); //保存x对应的y
int maxy = *xys[acts[i].x].rbegin(); //取x对应最大的y
if(acts[i].y == maxy) //添加新y成最大才要更新
update(1, acts[i].x, maxy); //单点更新x的maxy
}
else if(acts[i].op == "remove")
{
xys[acts[i].x].erase(acts[i].y); //删除x对应的y
int maxy = -1;
if(xys[acts[i].x].size())
maxy = *xys[acts[i].x].rbegin(); //取x对应最大的y
if(acts[i].y > maxy) //删除的y是最大的才要更新
update(1, acts[i].x, maxy); //单点更新x的maxy
}
else
{
//在[acts[i].x + 1, maxx]范围找到第一个maxy>acts[i].y的叶子节点的位置
int x = query(1, acts[i].x + 1, maxx, acts[i].y);
if(x == -1) cout << -1 << endl;
else
{
//在x对应的所有y中找第一个比acts[i].y大的值
int y = *xys[x].upper_bound(acts[i].y);
cout << allx[x-1] << " " << y << endl; //x要还原为离散化之前的值
}
}
}
return 0;
}