【题解】[JOISC2020] 扫除

solution:

果不愧是 JOISC 毒瘤题。

正确的思考方式:

如果只有往右扫的操作,那么操作的顺序就不重要了,那么一个点往右移动只在于能包含它的最能往右的操作。

如果还有往上扫的操作,那么一个高度为 l l l 的往右扫的操作,实际上只能把还没扫除范围的点往右移,注意到被扫除范围的点一定是 [ 1 , x ] [1,x] [1,x] 范围内的点(可以通过按顺序操作线段树维护),那么这个右扫的实际作用范围就是横坐标为 [ x + 1 , n − l ] [x+1,n-l] [x+1,nl] 的点。

把所有横向操作的影响范围预处理出来后,那么对于横向的操作的顺序又不重要了,可以按照 h h h 排序后,区间取 m a x max max 然后对于每个灰尘单点查询 m a x max max 即可得到横坐标的变化。

可以得到 64 p t s 64pts 64pts 。(

引入算法:线段树分治(类比cdq的恶心玩意):

为了去掉插入,考虑线段树分治,对于每个查询拆分成 log 个整区间,套用上述做法即可。

朴素做法 O ( ( m + q ) log ⁡ q log ⁡ n ) O((m+q)\log q\log n) O((m+q)logqlogn) 。使用离散化坐标可以得到更优的复杂度。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=2e6+5;
int n,m,q,sum,rt[2],ap[N],ax[N],ay[N],lc[N<<5],rc[N<<5],mx[N<<5];
vi lp,rp,ty,le,ansx,ansy;
inline int read() {
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
void clear() {
	rt[0]=rt[1]=sum=0;
}
bool cmpx(int x,int y) {
	return ansx[x]>ansx[y];
}
bool cmpy(int x,int y) {
	return ansy[x]>ansy[y];
}
void upd(int l,int r,int &pos,int L,int R,int x) {
	if(L>R) return;
	if(!pos) pos=++sum,lc[pos]=rc[pos]=mx[pos]=0;
	if(L<=l&&r<=R) return (void)(mx[pos]=max(mx[pos],x));
	int mid=l+r>>1;
	if(L<=mid) upd(l,mid,lc[pos],L,R,x);
	if(R>mid) upd(mid+1,r,rc[pos],L,R,x);
}
int qry(int l,int r,int &pos,int x) {
	if(!pos) return 0;
	if(l==r) return mx[pos];
	int mid=l+r>>1;
	//标记永久化 
	return max(mx[pos],x<=mid?qry(l,mid,lc[pos],x):qry(mid+1,r,rc[pos],x));
}
void solve(int l,int r,const vi &qu) {
	if(qu.empty()) return;
	clear();
	vi lq,rq,nq;
	vector<pair<int,int>> v[2];
	int mid=l+r>>1;
	for(int i=l,t;i<=r;i++) {
		t=ty[i];
		v[t].pb(make_pair(-le[i],qry(0,n,rt[t^1],le[i])));
		upd(0,n,rt[t],v[t].back().se,n-le[i]-1,le[i]+1);
	}
	for(int i=0;i<qu.size();i++) {
		if(lp[qu[i]]<=l&&r<=rp[qu[i]]) {
			nq.pb(qu[i]);
		}
		else {
			if(lp[qu[i]]<=mid) lq.pb(qu[i]);
			if(rp[qu[i]]>mid) rq.pb(qu[i]);
		}
	}
	clear();
	sort(v[0].begin(),v[0].end());
	sort(nq.begin(),nq.end(),cmpy);
	for(int i=0,j=0;i<nq.size();i++) {
		for(;j<v[0].size()&&-v[0][j].fi>=ansy[nq[i]];j++) {
			upd(0,n,rt[0],v[0][j].se,n+v[0][j].fi,n+v[0][j].fi);
		}
		ansx[nq[i]]=max(ansx[nq[i]],qry(0,n,rt[0],ansx[nq[i]]));
	}
	sort(v[1].begin(),v[1].end());
	sort(nq.begin(),nq.end(),cmpx);
	for(int i=0,j=0;i<nq.size();i++) {
		for(;j<v[1].size()&&-v[1][j].fi>=ansx[nq[i]];j++) {
			upd(0,n,rt[1],v[1][j].se,n+v[1][j].fi,n+v[1][j].fi);
		}
		ansy[nq[i]]=max(ansy[nq[i]],qry(0,n,rt[1],ansy[nq[i]]));
	}
	solve(l,mid,lq);
	solve(mid+1,r,rq);
}
signed main() {
//	freopen("data.in","r",stdin);
//	freopen("own.out","w",stdout);
	n=read(),m=read(),q=read();
	for(int i=1;i<=m;i++) ax[i]=read(),ay[i]=read();
	while(q--) {
		int op=read(),x=read();
		if(op==1) lp.pb(ap[x]),rp.pb(ty.size()-1),ansx.pb(ax[x]),ansy.pb(ay[x]);
		else if(op==2) ty.pb(0),le.pb(x);
		else if(op==3) ty.pb(1),le.pb(x);
		else ax[++m]=x,scanf("%d",&ay[m]),ap[m]=ty.size();
	}
	vi qu;
	for(int i=0;i<lp.size();i++) {
		if(lp[i]<=rp[i]) {
			qu.pb(i);
		}
	}
	solve(0,ty.size()-1,qu);
	for(int i=0;i<lp.size();i++) {
		printf("%d %d\n",ansx[i],ansy[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值