算法1:
考虑没有插入操作怎么办。
先考虑sub3。
定义一个点在边界上:它的右上角没有任何点。
注意到无论怎么进行操作,在边界的点还是会在边界,且顺序不会交换。
所以可以用线段树二分简单维护。
再考虑sub4。
sub4和sub3的区别:在sub3中每个点都在边界上,但是sub4并不。
但是注意到如果一个点在边界上,则它在之后的操作它还是在边界上。
所以可以使用线段树+平衡树维护。
开平衡树维护边界上的点的x/y坐标。
不能用线段树,因为要支持插入操作。
在修改时在平衡树二分+平衡树区间赋值维护。
在操作时,可能有新的点到达边界。
使用两个线段树找点即可。
(这个部分还是不太懂)
由于每个点会被插入到平衡树中至多1次,所以时间复杂度是(O(nlog_2 n))
有插入操作时,注意到每个修改操作对答案的贡献是独立的,于是可以使用时间线段树规避删除。
算法2:
算法1太难写了,考虑一种更好写的算法。
考虑sub2,没有向上推的操作。
一个点的y坐标不会变,维护个关于x坐标的线段树即可。
拓展到sub4。
直接拓展会有一个问题。
一个操作相当于把区间([1,r])的点在线段树上坐标对(n-r)取(max)。
然而操作的范围不一定是([1,n-r])
比如我们要向上推。但是有一个比较小,但是距离特别长的向右推的操作把部分灰尘推走了。
所以一个操作的是把区间([x,r])的点的坐标对(n-r)取max。
我们要得到这个x。
维护两个线段树,维护(x,y)轴的答案。
按照顺序执行所有修改操作。
对于一个修改操作, 把对应的区间对(len)取max。然后单点查询另一颗线段树在len位置的值。
画图可以知道正确性。
在查询操作时,由于一个推距离(=x)的点只能影响到某个坐标(<=x)的点,所以把所有点按照坐标从大到小排序然后分两维做。
有插入操作时也可以时间线段树。
#include<bits/stdc++.h>
using namespace std;
#define N 1000010
int n,m,q,x[N],y[N],ql[N],qr[N],ct,cc,le[N],tp[N],ax[N],ay[N],t[N];
struct no{
int x,y;
};
vector<int>v[N*4];
vector<no>op[2];
int operator <(no x,no y){
return x.x>y.x||(x.x==y.x&&x.y>y.y);
}
int mx[N*40],cv,lc[N*40],rc[N*40],rt[2];
void up(int &o,int l,int r,int x,int y,int z){
if(!o)
o=++cv;
if(r<x||y<l)
return;
if(x<=l&&r<=y){
mx[o]=max(mx[o],z);
return;
}
int md=(l+r)/2;
up(lc[o],l,md,x,y,z);
up(rc[o],md+1,r,x,y,z);
}
int qu(int o,int l,int r,int x){
if(l==r)
return mx[o];
int md=(l+r)/2;
if(x<=md)
return max(qu(lc[o],l,md,x),mx[o]);
return max(qu(rc[o],md+1,r,x),mx[o]);
}
int cx(int x,int y){
return ax[x]>ax[y];
}
int cy(int x,int y){
return ay[x]>ay[y];
}
void ins(int o,int l,int r,int x,int y,int z){
if(r<x||y<l)
return;
if(x<=l&&r<=y){
v[o].push_back(z);
return;
}
int md=(l+r)/2;
ins(o*2,l,md,x,y,z);
ins(o*2+1,md+1,r,x,y,z);
}
void cl(){
for(int i=1;i<=cv;i++)
lc[i]=rc[i]=mx[i]=0;
rt[0]=rt[1]=cv=0;
}
void fz(int o,int l,int r){
op[0].clear();
op[1].clear();
cl();
for(int i=l;i<=r;i++){
int va=qu(rt[tp[i]^1],0,n,le[i]);
op[tp[i]].push_back((no){le[i],va});
up(rt[tp[i]],0,n,va,n-le[i]-1,le[i]+1);
}
cl();
sort(op[0].begin(),op[0].end());
sort(op[1].begin(),op[1].end());
sort(v[o].begin(),v[o].end(),cy);
int j=0;
for(int x:v[o]){
while(j<op[0].size()&&op[0][j].x>=ay[x]){
up(rt[0],0,n,op[0][j].y,n-op[0][j].x,n-op[0][j].x);
j++;
}
ax[x]=max(ax[x],qu(rt[0],0,n,ax[x]));
}
sort(v[o].begin(),v[o].end(),cx);
j=0;
for(int x:v[o]){
while(j<op[1].size()&&op[1][j].x>=ax[x]){
up(rt[1],0,n,op[1][j].y,n-op[1][j].x,n-op[1][j].x);
j++;
}
ay[x]=max(ay[x],qu(rt[1],0,n,ay[x]));
}
int md=(l+r)/2;
if(l>=r)return;
fz(o*2,l,md);
fz(o*2+1,md+1,r);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
t[i]=1;
}
for(int i=1;i<=q;i++){
int op;
scanf("%d",&op);
if(op==1){
int p;
scanf("%d",&p);
ax[++cc]=x[p];
ay[cc]=y[p];
qr[cc]=ct;
ql[cc]=t[p];
}
if(op==2){
int x;
scanf("%d",&x);
le[++ct]=x;
tp[ct]=0;
}
if(op==3){
int x;
scanf("%d",&x);
le[++ct]=x;
tp[ct]=1;
}
if(op==4){
int a,b;
scanf("%d%d",&a,&b);
x[++m]=a;
y[m]=b;
t[m]=ct+1;
}
}
for(int i=1;i<=cc;i++)
ins(1,1,ct,ql[i],qr[i],i);
fz(1,1,ct);
for(int i=1;i<=cc;i++)
printf("%d %d
",ax[i],ay[i]);
}