Description
在一个笛卡尔坐标系中,定义三种操作:
(add(x,y)),将点((x,y))标记在坐标系上
(find(x,y)),查询点((x,y))严格右上方中,横坐标最小的点。如果有多个,输出其中纵坐标最小的。没有则输出-1
(remove(x,y)),将点((x,y))取消标记
Input
第一行是操作个数(n)。
下面(n)行,每行对应一个操作。
Output
每个查询操作输出一个答案。
Hint
(n~leq~10^5,|x|,|y|~leq~10^9)。数据保证合法。
Solution
(x,y)这么大,先给他离散化再说。
考虑一个笛卡尔坐标系上,对每个横坐标(x)任取一个(y)组成点((x,y)),是与一个数列(A)有一一对应关系的。即,点((x,y))对应序列(A_x=y)。
考虑对于一次查询,一列能做为答案的必要条件是这一列上(y)轴最大的点大于被查询的点的纵坐标。于是就想到将每个(x)对应的最大的(y)写入序列。于是每次查询时在序列上查询大于(x)的后缀上第一个大于(y)的位置的下标。这个显然可以用线段树搞定。考虑剩下的(y)怎么记录。使用线段树可以查询出应该被选择的横坐标。则纵坐标就是这一列上大于(y)的第一个数。于是对于每一列开一个(set),维护这一列上所有的(y),查询时直接upper_bound即可。
考虑线段树的写法。对于线段树的一个区间,维护这段区间中最大值的下标是多少。查询时,先递归查询左区间,如果左区间不合法则递归查询右区间。一个区间不合法当且仅当他与被查询的区间无交或他的最大值小于被查询的值(k)。
考虑这么做的复杂度:因为一个区间会被线段树划分成(O(log(len)))个线段。发现这些遍历这些线段是(O(log))的,于是一次操作的复杂度是(O(log))的。总复杂度(O(nlogn))
Code
#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rg register
#define ci const int
#define cl const long long
typedef long long int ll;
template <typename T>
inline void qr(T &x) {
rg char ch=getchar(),lst=' ';
while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst == '-') x=-x;
}
namespace IO {
char buf[120];
}
template <typename T>
inline void qw(T x,const char aft,const bool pt) {
if(x < 0) {x=-x,putchar('-');}
rg int top=0;
do {IO::buf[++top]=x%10+'0';} while(x/=10);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template <typename T>
inline T mmax(const T a,const T b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}
template <typename T>
inline void mswap(T &_a,T &_b) {
T _temp=_a;_a=_b;_b=_temp;
}
const int maxn = 200010;
const int maxt = 1600010;
struct M {
int opt,x,y;
};
M MU[maxn];
int n,tcnt,num;
int temp[maxt],CU[maxt];
std::set<int>ss[maxt];
struct Tree {
int v;
inline void update(const Tree &_ls,const Tree _rs) {
if(!(~(_ls.v))) this->v = _rs.v;
else if(!(~(_rs.v))) this->v = _ls.v;
else {
if(CU[_ls.v] >= CU[_rs.v]) this->v=_ls.v;
else this->v=_rs.v;
}
}
};
Tree tree[maxt];
void init_hash();
void change(ci,ci,ci,ci);
int ask(ci,ci,ci,ci,ci);
int main() {
qr(n);
for(rg int i=1;i<=n;++i) {
M &now=MU[i];
rg char ch=getchar();
while((ch > 'z') || (ch < 'a')) ch=getchar();
if(ch == 'a') now.opt=1;
else if(ch == 'r') now.opt=3;
else now.opt=2;
qr(now.x);qr(now.y);
temp[++tcnt]=now.x;temp[++tcnt]=now.y;
}
init_hash();
memset(tree,-1,sizeof tree);
for(rg int i=1;i<=n;++i) {
M &now=MU[i];
switch(now.opt) {
case 1: {
ss[now.x].insert(now.y);
if(now.y == *(--ss[now.x].end())) {CU[now.x]=now.y;change(1,num,1,MU[i].x);}
break;
}
case 2: {
int k=ask(1,num,1,now.x+1,now.y);
if(!(~k)) {puts("-1");break;}
qw(temp[k],' ',true);
std::set<int>::iterator zay = ss[k].upper_bound(now.y);
qw(temp[*zay],'
',true);
break;
}
case 3: {
if(now.y == *(--ss[now.x].end())) {
ss[now.x].erase(now.y);
if(ss[now.x].empty()) CU[now.x]=0;
else CU[now.x]=*(--ss[now.x].end());
change(1,num,1,MU[i].x);
}
else ss[now.x].erase(now.y);
break;
}
}
}
return 0;
}
void init_hash() {
std::sort(temp+1,temp+1+tcnt);s
int *ed=std::unique(temp+1,temp+1+tcnt);
num=ed-temp-1;
for(rg int i=1;i<=n;++i) {
MU[i].x=std::lower_bound(temp+1,ed,MU[i].x)-temp;
MU[i].y=std::lower_bound(temp+1,ed,MU[i].y)-temp;
}
}
void change(ci l,ci r,ci p,ci aim) {
if(l > r) return;
if((l > aim) || (r < aim)) return;
if(l == r) {tree[p].v=l;return;}
int mid=(l+r)>>1,dp=p<<1,ddp=dp|1;
change(l,mid,dp,aim);change(mid+1,r,ddp,aim);
tree[p].update(tree[dp],tree[ddp]);
}
int ask(ci l,ci r,ci p,ci aim,ci v) {
if(l > r) return -1;
if(r < aim) return -1;
if(!(~(tree[p].v))) return -1;
if(CU[tree[p].v] <= v) return -1;
if(l == r) return tree[p].v;
int mid=(l+r)>>1,dp=p<<1,ddp=dp|1;
if(mid >= r) return ask(l,mid,dp,aim,v);
else if(mid < l) return ask(mid+1,r,ddp,aim,v);
else {
int _ans;
if(~(_ans=ask(l,mid,dp,aim,v))) return _ans;
else return ask(mid+1,r,ddp,aim,v);
}
}
Solution
一个笛卡尔坐标系中对于每一个横坐标(x)选择一个纵坐标(y)后,可以将之一一对应到一个下标为(x),值为(y)的线段上。利用这个性质可以将坐标问题改为序列修改问题使用数据结构处理。