题解:P11695 [JRKSJ ExR] 昼寝

\(\text{Link}\)

题意

给定 \(n,m\),你需要维护一个 \([1,n)\) 的数轴上区间的初始为空的可重集合,支持三种操作共 \(m\) 次:

  1. 插入一个区间 \([l,r)\)
  2. 删除第 \(t\) 次操作插入的区间。
  3. 给出一个区间 \([l,r)\),判断当前可重集合中是否存在一个子集,使得子集中所有区间的并恰好是 \([l,r)\)

\(n\le 10^6\)\(m\le 5\times 10^5\)

题解

本题解中默认将题目中给出的区间的右端点减一变为闭区间。

静态情况下的问题考虑扫描线。扫描右端点,维护一个数组 \(c_{1\sim r}\),其中 \(c_i\) 表示包含 \(i\) 的区间的左端点的最大值,那么一个区间 \([l,r]\) 能被表示出当且仅当 \(\min_{i=l}^rc_i=l\)


一个询问区间 \([ql,qr]\) 能被表示出当且仅当所有是 \([ql,qr]\) 的子区间的操作区间 \([l,r]\) 的并恰等于 \([ql,qr]\)

对于动态的情况,每个操作区间 \([l,r]\) 有其存在的时间区间 \([tl,tr]\),每个询问区间 \([ql,qr]\) 也有其时间 \(qt\)

考虑序列分治,设现在正在处理区间 \([cl,cr]\),我们尝试解决所有跨过区间中点 \(mid\) 的询问 \([ql,qr]\)

操作区间有被左半边包含、被右半边包含和跨过区间中点三类。


对于跨过区间中点的一类,我们按顺序扫描时间轴。

这一类区间对每个询问的贡献为左区间的一个后缀和右区间的一个前缀。

对每个询问 \([ql,qr]\),这部分贡献的右区间前缀即为这个时间点存在的所有满足 \(r\le qr,l\ge ql\) 的操作区间 \([l,r]\) 中最大的 \(r\)

建一颗线段树,其位置 \(p\) 存储右端点为 \(r\) 的区间中左端点的最大值,进行一次线段树二分即可找到位置 \(p\)。左半区间的部分同理。


接下来是被半边包含的情况,不妨考虑右半区间的一类。对于一个询问区间 \([ql,qr]\),我们要求出最大的位置 \(mid<p\le qr\) 使得位置 \(p\) 不能被这一类区间覆盖。

接下来的思路是,对于单次询问,可以从右往左依次枚举直到找到一个位置 \(p\) 使得其未被覆盖。于是考虑将所有询问一起做这个过程,扫到一个位置 \(p\) 时取出所有在这个位置未被覆盖的询问。

按照 \(i=r,r-1,\dots\) 的顺序进行扫描线,在时间轴上维护一个数组 \(f_{1\sim t}\),扫到一个询问区间的右端点时我们将这个询问放至其对应时间点的位置进行维护,即令 \(f_{qt}\gets i\),扫到一个操作区间 \([l,i]\) 的右端点时将其对应时间区间的 \(f\) 值对 \(l-1\)\(\min\)。扫到 \(i\) 时,取出所有 \(f_{qt}=i\) 的询问即可。

这可以使用线段树轻松实现。


根据三部分求出的信息,我们便可判断每个询问区间是否可被表示出了。

每个操作区间会在 \(O(\log n)\) 个分治区间中作出贡献,故时间复杂度 \(O(n\log n+m\log^2n)\),可以通过。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
    //read()
}
#define pii pair<int,int>
#define mpr make_pair
#define fir first
#define sec second
#define vci vector<int>
const int N=1e6+10,M=5e5+10,INF=1e9+10;
int n,m;
int tl[M],tr[M],tpl[M],tpr[M],L[M],R[M];
int rl1[M],rr1[M],rl2[M],rr2[M],ans[M];
vci vl[N],vt[N],vi[M],vd[M],vq[M];
template<class ND>
struct Set{
	priority_queue<ND>p,q;
	inline void upd(){ while(!q.empty()&&p.top()==q.top()) p.pop(),q.pop(); }
	public:
	inline int size(){ return p.size()-q.size(); }
	inline bool empty(){ return p.size()==q.size(); }
	inline void ins(ND v){ p.push(v); }
	inline void del(ND v){ q.push(v); }
	inline ND top(){ upd();return p.top(); }
	inline void pop(){ upd();p.pop(); }
};
Set<int>sl[N],sr[N];
#define ls (rt<<1)
#define rs (rt<<1|1)
struct Segment_Tree1{
	int mn[N<<1];
	inline void pushup(int rt){
		mn[rt]=min(mn[ls],mn[rs]);
	}
	inline void build(int rt,int l,int r){
		mn[rt]=INF;
		if(l==r) return ;
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(rt);
	}
	inline void update(int rt,int l,int r,int p){
		if(l==r){
			mn[rt]=sl[l].size()?-sl[l].top():INF;
			return ;
		}
		int mid=l+r>>1;
		if(p<=mid) update(ls,l,mid,p);
		else update(rs,mid+1,r,p);
		pushup(rt);
	}
	inline int find(int rt,int l,int r,int L,int R,int v){
		if(r<L||l>R||mn[rt]>v) return -1;
		if(l==r) return l;
		int mid=l+r>>1,tp=find(ls,l,mid,L,R,v);
		if(tp==-1) tp=find(rs,mid+1,r,L,R,v);
		return tp;
	}
}Tl; 
struct Segment_Tree2{
	int mx[N<<1];
	inline void pushup(int rt){
		mx[rt]=max(mx[ls],mx[rs]);
	}
	inline void build(int rt,int l,int r){
		mx[rt]=0;
		if(l==r) return ;
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(rt);
	}
	inline void update(int rt,int l,int r,int p){
		if(l==r){
			mx[rt]=sr[l].size()?sr[l].top():0;
			return ;
		}
		int mid=l+r>>1;
		if(p<=mid) update(ls,l,mid,p);
		else update(rs,mid+1,r,p);
		pushup(rt);
	}
	inline int find(int rt,int l,int r,int L,int R,int v){
		if(r<L||l>R||mx[rt]<v) return -1;
		if(l==r) return l;
		int mid=l+r>>1,tp=find(rs,mid+1,r,L,R,v);
		if(tp==-1) tp=find(ls,l,mid,L,R,v);
		return tp;
	}
}Tr; 
struct Segment_Tree3{
	pii mn[M<<2];
	int tag[N<<2];
	inline void pushup(int rt){
		mn[rt]=min(mn[ls],mn[rs]);
	}
	inline void pushtag(int rt,int v){
		mn[rt].fir=max(mn[rt].fir,v);
		tag[rt]=max(tag[rt],v); 
	}
	inline void pushdown(int rt){
		if(!tag[rt]) return ;
		pushtag(ls,tag[rt]);
		pushtag(rs,tag[rt]);
		tag[rt]=0;
	}
	inline void build(int rt,int l,int r){
		tag[rt]=0,mn[rt]=mpr(INF,0);
		if(l==r) return ;
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
	}
	inline void ins(int rt,int l,int r,int p,pii v){
		if(l==r){
			mn[rt]=v;
			return ;
		}
		pushdown(rt);
		int mid=l+r>>1;
		if(p<=mid) ins(ls,l,mid,p,v);
		else ins(rs,mid+1,r,p,v);
		pushup(rt);
	}
	inline void upd(int rt,int l,int r,int L,int R,int v){
		if(L<=l&&r<=R) return pushtag(rt,v);
		pushdown(rt);
		int mid=l+r>>1;
		if(L<=mid) upd(ls,l,mid,L,R,v);
		if(R>mid) upd(rs,mid+1,r,L,R,v);
		pushup(rt);
	}
	inline pii query(){
		return mn[1];
	}
}T1;
struct Segment_Tree4{
	pii mx[M<<2];
	int tag[N<<2];
	inline void pushup(int rt){
		mx[rt]=max(mx[ls],mx[rs]);
	}
	inline void pushtag(int rt,int v){
		mx[rt].fir=min(mx[rt].fir,v);
		tag[rt]=min(tag[rt],v); 
	}
	inline void pushdown(int rt){
		if(tag[rt]==INF) return ;
		pushtag(ls,tag[rt]);
		pushtag(rs,tag[rt]);
		tag[rt]=INF;
	}
	inline void build(int rt,int l,int r){
		tag[rt]=INF,mx[rt]=mpr(-INF,0);
		if(l==r) return ;
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
	}
	inline void ins(int rt,int l,int r,int p,pii v){
		if(l==r){
			mx[rt]=v;
			return ;
		}
		pushdown(rt);
		int mid=l+r>>1;
		if(p<=mid) ins(ls,l,mid,p,v);
		else ins(rs,mid+1,r,p,v);
		pushup(rt);
	}
	inline void upd(int rt,int l,int r,int L,int R,int v){
		if(L<=l&&r<=R) return pushtag(rt,v);
		pushdown(rt);
		int mid=l+r>>1;
		if(L<=mid) upd(ls,l,mid,L,R,v);
		if(R>mid) upd(rs,mid+1,r,L,R,v);
		pushup(rt);
	}
	inline pii query(){
		return mx[1];
	}
}T2;
inline void solve(int l,int r,vci op,vci qr){
	if(l>r||!qr.size()||!op.size()) return ;
	if(l==r) return ;
	int mid=l+r>>1;
//	printf("---solve(%d,%d,%d)---\n",l,r,mid);
	vci opl,opm,opr,qrl,qrm,qrr;
	for(auto tp:op){
		if(R[tp]<=mid) opl.push_back(tp);
		else if(L[tp]>mid) opr.push_back(tp);
		else opm.push_back(tp);
	}
	for(auto tp:qr){
		if(R[tp]<=mid) qrl.push_back(tp);
		else if(L[tp]>mid) qrr.push_back(tp);
		else qrm.push_back(tp);
	}
	//对时间维离散化
	vci lsh;
	for(auto i:op) lsh.push_back(tpl[i]=tl[i]),lsh.push_back(tpr[i]=tr[i]);
	for(auto i:qrm) lsh.push_back(tl[i]);
	sort(lsh.begin(),lsh.end());
	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
	int k=lsh.size();
	for(auto i:op) tl[i]=lower_bound(lsh.begin(),lsh.end(),tl[i])-lsh.begin()+1;
	for(auto i:op) tr[i]=lower_bound(lsh.begin(),lsh.end(),tr[i])-lsh.begin()+1;
	for(auto i:qrm) tl[i]=lower_bound(lsh.begin(),lsh.end(),tl[i])-lsh.begin()+1;
	//计算跨 mid 区间对跨 mid 询问 
	for(int p:opm) 
		vi[tl[p]].push_back(p),
		vd[tr[p]].push_back(p);
	for(int p:qrm)
		vq[tl[p]].push_back(p);
	Tl.build(1,l,mid),Tr.build(1,mid+1,r);
	for(int i=1;i<=k;i++){
		for(auto p:vi[i])
			sl[L[p]].ins(-R[p]),
			sr[R[p]].ins(L[p]),
			Tl.update(1,l,mid,L[p]),
			Tr.update(1,mid+1,r,R[p]);
		for(auto p:vq[i]){
			int tp=Tl.find(1,l,mid,L[p],mid,R[p]);
			rl1[p]=tp==-1?mid+1:tp;
			tp=Tr.find(1,mid+1,r,mid+1,R[p],L[p]);
			rr1[p]=tp==-1?mid:tp;
		}
		for(auto p:vd[i])
			sl[L[p]].del(-R[p]),
			sr[R[p]].del(L[p]),
			Tl.update(1,l,mid,L[p]),
			Tr.update(1,mid+1,r,R[p]);
	}
	for(auto p:qrm)
		rl2[p]=mid+1,rr2[p]=mid;
	//计算左区间对跨 mid 询问
	T1.build(1,1,k);
	for(int p:qrm)
		vt[L[p]].push_back(p);
	for(int p:opl)
		vl[L[p]].push_back(p);
	for(int i=l;i<=mid;i++){
		for(auto p:vt[i])
			T1.ins(1,1,k,tl[p],mpr(i,p));
		for(auto p:vl[i])
			T1.upd(1,1,k,tl[p],tr[p],R[p]+1);
		while(1){
			pii tp=T1.query();
			if(tp.fir>i) break;
			int p=tp.sec;
			rl2[p]=i;
			T1.ins(1,1,k,tl[p],mpr(INF,0));
		}
	}
	//计算右区间对跨 mid 询问
	T2.build(1,1,k);
	for(int p:qrm)
		vt[R[p]].push_back(p);
	for(int p:opr)
		vl[R[p]].push_back(p);
	for(int i=r;i>mid;i--){
		for(auto p:vt[i])
			T2.ins(1,1,k,tl[p],mpr(i,p));
		for(auto p:vl[i])
			T2.upd(1,1,k,tl[p],tr[p],L[p]-1);
		while(1){
			pii tp=T2.query();
			if(tp.fir<i) break;
			int p=tp.sec;
			rr2[p]=i;
			T2.ins(1,1,k,tl[p],mpr(-INF,0));
		}
	}
	for(auto p:qrm)
		ans[p]=(rl2[p]>=rl1[p])&&(rr2[p]<=rr1[p]);
	//清空+递归 
	for(int i=1;i<=k;i++)
		vi[i].clear(),vd[i].clear(),vq[i].clear();
	for(int i=l;i<=r;i++)
		vt[i].clear(),vl[i].clear();
	for(auto p:opl) tl[p]=tpl[p],tr[p]=tpr[p];
	for(auto p:opr) tl[p]=tpl[p],tr[p]=tpr[p];
	solve(l,mid,opl,qrl),solve(mid+1,r,opr,qrr);
}
int ct[N];
int main(){
//	freopen("1.in","r",stdin);
//	freopen("1.ans","w",stdout);
	n=read(),m=read();
	vci Op,Qr;
	R[0]=INF;
	for(int i=1;i<=m;i++){
		int op=read();
		if(op==2){
			int t=read();
			tr[t]=i-1;
			if(L[t]==R[t]) ct[L[t]]--;
		}else{
			int l=read(),r=read()-1;
			L[i]=l,R[i]=r,tl[i]=i,tr[i]=m;
			if(l==r){
				if(op==1) ct[l]++;
				else ans[i]=!!ct[l];
			}
			if(op==1) Op.push_back(i);
			else Qr.push_back(i);
		}
	}
	solve(1,n-1,Op,Qr);
	for(int i:Qr)
		putc(ans[i]?'Y':'N'),putc('\n');
	flush();
}
posted @ 2025-05-28 11:44  ffffyc  阅读(14)  评论(0)    收藏  举报