【JOISC2020】扫除
【JOISC2020】扫除
Description
由于 Bitaro AK 了 IOI,所以 IOI 主办方送了他一套房子,为一个边长为 \(N\) 的等腰直角三角形。房间内一点用坐标 \((x,y)\) 表示,其中 \(0\leq x+y\leq N\)。直角顶点为原点,三角形两腰分别为 \(x\) 轴与 \(y\) 轴。
一天,Bitaro 发现自己已经 AK 了 1919810 届 IOI 闲的没事做准备打扫房间里的灰尘。这些灰尘一开始一共有 \(M\) 堆,其中第 \(i\) 堆位于 \((X_i,Y_i)\)。同时,可能存在多堆灰尘位于同一个位置上的情况。
现在 Bitaro 准备用扫帚打扫房间。我们认为扫帚是放置在房间里的一条线段,并且将这条线段的长度称为扫帚的宽度。由于 Bitaro 很有条理,所以他只会用以下的两种方式打扫房间:
-
Bitaro 将扫帚平行于 \(x\) 轴放置,一端位于原点。然后他会水平向上移动扫帚,直到不能移动为止。如果扫帚的宽度为 \(l\),那么原来一堆满足 \(x<N-l,y\leq l\) 的灰尘 \((x,y)\) 将会被移动到 \((N-l,y)\)。(这个位置可能会存在多堆灰尘)我们称这个过程为过程 H。
-
Bitaro 将扫帚平行于 \(y\) 轴放置,一端位于原点。然后他会垂直向右移动扫帚,直到不能移动为止。如果扫帚的宽度为 \(l\),那么原来一堆满足 \(x\leq l,y<N-l\) 的灰尘 \((x,y)\) 将会被移动到 \((x,N-l)\)。(这个位置可能会存在多堆灰尘)我们称这个过程为过程 V。
在 Bitaro 的房间里,依次会发生 \(Q\) 个事件。第 \(i\) 个事件形如以下 \(4\) 种:
-
Bitaro 想要计算第 \(P_i\) 堆灰尘的位置坐标;
-
Bitaro 使用宽度为 \(L_i\) 的扫帚,进行了过程 H;
-
Bitaro 使用宽度为 \(L_i\) 的扫帚,进行了过程 V;
-
有一堆新的灰尘出现在点 \((A_i,B_i)\) 处。如果在这个事件之前一共有 \(c\) 堆灰尘,那么这堆灰尘就是房间中的第 \(c+1\) 堆灰尘。
由于 Bitaro 已经 AK 了 IOI,啥都不想干,所以你需要写一个程序,给出房间的腰长,每一堆灰尘的位置坐标和每个事件的细节,求出要求的某堆灰尘的位置坐标。
Input
第一行三个整数,分别为 \(N,M,Q\)。
接下来 \(m\) 行,每行两个整数 \(X_i,Y_i\),表示第 \(i\) 堆灰尘一开始的位置。
接下来 \(Q\) 行,每行两到三个整数,表示一个事件。设 \(T_i\) 为第一个整数,每行含义如下:
-
如果 \(T_i=1\),则这行有两个整数 \(T_i,P_i\),表示 Bitaro 要计算第 \(P_i\) 堆灰尘的坐标;
-
如果 \(T_i=2\),则这行有两个整数 \(T_i,L_i\),表示 Bitaro 用宽度为 \(L_i\) 的扫帚进行了过程 H;
-
如果 \(T_i=3\),则这行有两个整数 \(T_i,L_i\),表示 Bitaro 用宽度为 \(L_i\) 的扫帚进行了过程 V;
-
如果 \(T_i=4\),则这行有三个整数 \(T_i,A_i,B_i\),表示一堆新的灰尘出现在 \((A_i,B_i)\) 位置。
Output
对于每个 \(T_i=1\) 的事件,输出一行两个整数,表示事件 \(i\) 发生时第 \(P_i\) 堆灰尘的位置坐标。
Sample Input
6 2 10
1 1
4 0
4 2 3
3 3
1 1
4 1 2
2 3
2 0
1 4
3 2
1 3
1 2
Sample Output
1 3
3 2
3 3
6 0
Data Constraint
对于 \(100\%\) 的数据,\(1\leq n\leq 10^9,1\leq m\leq 5\times 10^5,1\leq Q\leq 10^6\)。保证:
-
\(0\leq X_i,Y_i\leq N,X_i+Y_i\leq N(1\leq i\leq m)\);
-
\(1\leq P_i\leq M^\prime(1\leq i\leq Q)\),其中 \(M^\prime\) 表示事件 \(i\) 发生时灰尘的堆数;
-
\(0\leq L_i\leq n-1(1\leq i\leq Q)\);
-
\(0\leq A_i,B_i\leq n,A_i+B_i\leq n(1\leq i\leq Q)\);
-
至少存在一个 \(T_i=1\) 的事件。
Solution
可以从最简单的情况入手
假设只有一种修改,所有询问在修改后,同时没有插入点
显然只要在线段树上改一改扫一扫就行了
现在考虑有两种修改
可以发现的是 V 和 H 可以互相影响,但 V 操作内部独立,H 操作同理
然后考虑求出每个操作所影响的最小坐标\(p\)
显然一个 V 会让\([p,n-l-1]\)内的 H \(\max\)上\(l+1\)
可以线段树维护
最后考虑插入
很容易可以求出一个询问所用的修改区间
所以直接线段树分治就行了
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 1000010
#define M 20000000
int n,m,q,vis[N],lst[N],oph[N],opv[N],c1,c2,cnt;
struct point{int x,y,num;}p[N],val[N],a[N],s1[N],s2[N];
int ls[M],rs[M],tag[M],tot;
struct tree{
int root;
void ul(int x){if(!ls[x])ls[x]=++tot;}
void ur(int x){if(!rs[x])rs[x]=++tot;}
void change(int x,int l,int r,int ll,int rr,int v){
if(r<ll||l>rr)return;
if(l>=ll&&r<=rr){tag[x]=max(tag[x],v);return;}
int mid=l+r>>1;
ul(x);change(ls[x],l,mid,ll,rr,v);
ur(x);change(rs[x],mid+1,r,ll,rr,v);
}
int query(int x,int l,int r,int pos){
if(!x)return 0;
if(l==r)return tag[x];
int mid=l+r>>1;
return max(tag[x],pos<=mid?query(ls[x],l,mid,pos):query(rs[x],mid+1,r,pos));
}
void build(){root=++tot;}
}t[2];
bool cmp1(point x,point y){return x.x<y.x;}
bool cmp2(point x,point y){return x.y<y.y;}
struct Divide{
#define Ls x<<1
#define Rs (x<<1)|1
vector<int>ask[N*4];
void insert(int x,int l,int r,int ll,int rr,int id){
if(r<ll||l>rr)return;
if(l>=ll&&r<=rr){ask[x].push_back(id);return;}
int mid=l+r>>1;
insert(Ls,l,mid,ll,rr,id);insert(Rs,mid+1,r,ll,rr,id);
}
void solve(int x,int l,int r){
int h;
t[0].build();t[1].build();
c1=c2=0;
F(i,l,r){
if(oph[i]!=-1){
int tmp=t[0].query(t[0].root,0,n,oph[i]);
s1[++c1]=(point){tmp,oph[i],n-oph[i]};
t[1].change(t[1].root,0,n,tmp,n-oph[i]-1,oph[i]+1);
}
if(opv[i]!=-1){
int tmp=t[1].query(t[1].root,0,n,opv[i]);
s2[++c2]=(point){tmp,opv[i],n-opv[i]};
t[0].change(t[0].root,0,n,tmp,n-opv[i]-1,opv[i]+1);
}
}
F(i,1,tot)tag[i]=ls[i]=rs[i]=0;
tot=0;
t[0].build();t[1].build();
cnt=0;
for(auto d:ask[x])a[++cnt]=val[d];
sort(s1+1,s1+c1+1,cmp1);
sort(a+1,a+cnt+1,cmp1);
h=0;
F(i,1,cnt){
while(s1[h+1].x<=a[i].x&&h+1<=c1){
h++;
t[0].change(t[0].root,0,n,0,s1[h].y,s1[h].num);
}
int Q=t[0].query(t[0].root,0,n,a[i].y);
val[a[i].num].x=max(val[a[i].num].x,Q);
}
sort(s2+1,s2+c2+1,cmp1);
sort(a+1,a+cnt+1,cmp2);
h=0;
F(i,1,cnt){
while(s2[h+1].x<=a[i].y&&h+1<=c2){
h++;
t[1].change(t[1].root,0,n,0,s2[h].y,s2[h].num);
}
int Q=t[1].query(t[1].root,0,n,a[i].x);
val[a[i].num].y=max(val[a[i].num].y,Q);
}
F(i,1,tot)tag[i]=ls[i]=rs[i]=0;
tot=0;
if(l==r)return;
int mid=l+r>>1;
solve(Ls,l,mid);solve(Rs,mid+1,r);
}
}T;
int main(){
scanf("%d%d%d",&n,&m,&q);
memset(oph,-1,sizeof(oph));
memset(opv,-1,sizeof(opv));
F(i,1,m)scanf("%d%d",&p[i].x,&p[i].y),p[i].num=i,lst[i]=1;
F(i,1,q){
int op,ti,x,y;
scanf("%d",&op);
if(op==1)vis[i]=1,scanf("%d",&x),T.insert(1,1,q,lst[x],i,i),val[i]=p[x],val[i].num=i;
if(op==2)scanf("%d",&x),oph[i]=x;
if(op==3)scanf("%d",&x),opv[i]=x;
if(op==4)scanf("%d%d",&x,&y),m++,p[m]=(point){x,y,m},lst[m]=i;
}
T.solve(1,1,q);
F(i,1,q)if(vis[i]==1)printf("%d %d\n",val[i].x,val[i].y);
return 0;
}