Codeforces Beta Round #19
D-Points
题目
题目大意
和以前在POJ做的的数星星差不多,POJ数星星,这里也是在笛卡尔坐标系上有多个点,只不过现在对点有增删操作,并且最一开始坐标系上是没有点的,并且查找的是该最靠近查找点的最左下的点。
样例
输入样例
case 1:
7
add 1 1
add 3 4
find 0 0
remove 1 1
find 0 0
add 1 1
find 0 0
case 2:
13
add 5 5
add 5 6
add 5 7
add 6 5
add 6 6
add 6 7
add 7 5
add 7 6
add 7 7
find 6 6
remove 7 7
find 6 6
find 4 4
输出样例
case 1:
1 1
3 4
1 1
case 2:
7 7
-1
5 5
题解
我们遇到的问题
- 用什么数据结构去维护我们的坐标系?
- 怎样准确并快速的搜索到我们的答案搜索到?
- 由于该问题发生在二维笛卡尔坐标系,如果我们利用二维数据结构一定会浪费大量的空间,甚至发生内存的溢出,如何将二维数据结构,转换成一维的?
- 数据大小1e9难道我们要开1e9的空间维护数据吗?
解决方法
- 首先对于数据的增改查,我们首先想到的是利用树状数组或者是线段树进行数据的维护,其效率应该是最高的。我们选择用树状数组,因为线段树的代码有点长( •̀ ω •́ )✧
- 对于答案的搜索,我们可以遍历每一个管辖区间,利用二分查找寻找我们的最优解,由于树状数组本身的特性加上我们的查找是基于二分的方法来找的所以复杂度不会太高
- 如何将二维树状数组转换为一维树状数组?我们建立树状数组可以单单只利用y坐标,每个树状数组里存一个set集合,对应的set集合里就是对应y坐标的所有坐标点。为什么要用y不用x,这是从数星星那个题得出的经验
- 在创建和维护树状数组的时候,需要对 起点为
N - a.y
的点到x-lowbit(x)
的点以步长为x+lowbit(x)
进行维护,即需要维护所有包含当前节点a.y
的管辖区间的值。其中N 是该数组的最大长度、维护树状数组的节点
这里看不懂的建议再仔细研究一下树状数组的基本结构 由于起点为N - a.y
,所以数组的最大长度应该大于a.y
而我们的a.y
代表的是纵坐标 (我们上一步说利用纵坐标来建立树状数组 ) ,而纵坐标最大时1e9显然时会炸内存。 这里就需要利用离散化,下面时离散化的步骤:- 首先将所有的纵坐标排一下序,并且去重
- 离散化后的值就是,排好序后其对应的下标,可以利用二分查找寻找下标
- 将离散化后的值和原始数值进行一个映射处理,方便最后得出的答案不会受到影响
- 将原始数值彻底更新为离散化后的值
//b是存储的纵坐标,v里放的是坐标,mp是映射
sort(b.begin(),b.end());
int m = unique(b.begin(),b.end()) - b.begin();
for (int i = 0; i < v.size(); ++i) {
int pos = lower_bound(b.begin(),b.begin()+m,v[i].y) - b.begin();
mp[pos] = v[i].y; //不要用map,会超时,可以考虑用unordered_map
v[i].y = pos;
}
代码
#include<bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&(-x))
#define fa(x) (x+lowbit(x))
#define left(x) (x-lowbit(x))
#define int long long
using namespace std;
const int N = 200000+10;
const int INF = 0x3f3f3f3f;
set<pair<int,int>> sum[N];
unordered_map<int, int> mp;
struct Point{
int x,y;
};
//增加节点
void add(Point a){
int p = N - a.y;
for( ; p < N; p = fa(p) )
sum[p].insert({a.x,a.y});
}
//删除节点
void del(Point a){
int p = N - a.y;
for( ; p < N; p = fa(p) )
sum[p].erase({a.x,a.y});
}
//查询节点
void query(Point a){
pair<int,int> ans = {INF,INF};
set<pair<int,int>>::iterator it;
//对小于a.y每个管辖区间进行遍历并进行二分查找寻找最优解
//为什么是小于a.y的,因为大于a.y肯定不符合条件嘛
for( int i = N-a.y-1; i; i -= lowbit(i) ){
it = sum[i].lower_bound({a.x+1,a.y});
if(it!=sum[i].end()) ans = min(ans,*it);
}
if(ans.first == INF && ans.second == INF) printf("-1\n");
else printf("%lld %lld\n",ans.first,mp[ans.second]);
}
signed main(){
//std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;
scanf("%lld",&T);
vector<Point> v;
vector<char> op;
vector<int> b;
for (int i = 0; i < T; ++i) {
string c;
int x,y;
Point a;
cin>>c;
scanf("%lld%lld",&x,&y);
a.x = x; a.y = y;
v.push_back(a);
op.push_back(c[0]);
b.push_back(a.y);
}
//离散化
sort(b.begin(),b.end());
int m = unique(b.begin(),b.end()) - b.begin();
for (int i = 0; i < v.size(); ++i) {
int pos = lower_bound(b.begin(),b.begin()+m,v[i].y) - b.begin();
mp[pos] = v[i].y;
v[i].y = pos;
}
for (int i = 0; i < T; ++i) {
if(op[i] == 'a') add(v[i]);
if(op[i] == 'r') del(v[i]);
if(op[i] == 'f') query(v[i]);
}
return 0;
}