P7476「C.E.L.U-02」苦涩 题解

P7476 「C.E.L.U-02」苦涩

题目大意:

有n个可重集合,初始为空;将执行 m m m 次操作;

操作有 3 3 3 种:

  1. l l l r r r 的集合中加入一个元素 k k k

  2. 删除 l l l r r r 集合中最大的元素,若该集合内有多个最大元素,则只删除一个,若 l l l r r r 集合都无元素,则无视

  3. 查询 l l l r r r 集合中最大的元素,若 l l l r r r 集合都无元素,则输出 − 1 -1 1

要求输出所有查询操作的答案。

算法一:暴力修改查询

维护 n n n m u l t i s e t multiset multiset ,每次操作遍历 l l l r r r 的集合,暴力修改+查询。

时间复杂度: O ( n m ) O(nm) O(nm),期望得分: 10 p t s 10pts 10pts

算法二:特殊性质A

对于 s u b t a s k 2 subtask2 subtask2 来说,问题变成了区间赋较大值以及区间查最大值。

线段树维护区间最大,时间复杂度: O ( n log ⁡ n ) O(n\log{n}) O(nlogn),期望得分: 20 p t s 20pts 20pts

算法三:特殊性质B法一

对于 s u b t a s k 3 , 5 subtask3,5 subtask3,5 来说,原先的区间删最大变成了单点删最大。

考虑分块,在每个块和点中维护一个大根堆,记录块内最大值。

删除时找到区间内最大的块,遍历块内单点,比较单点中的堆的堆顶和块中的堆的堆顶,

若单点中的更大,那么直接删除,更新单点和块内的最大值;

否则,删除后将块内每个堆都加上这个点的堆,无需更新最大值。

时间复杂度: O ( n n log ⁡ n ) O(n\sqrt{n}\log{n}) O(nn logn),期望得分: 30 p t s 30pts 30pts

算法四:特殊性质B法二

有了大根堆的想法,那么我们可以在线段树的最底层节点处维护大根堆,并记录最大值

每次删除,一直搜到底, O ( log ⁡ n ) O(\log{n}) O(logn) 修改加上堆的 O ( log ⁡ n ) O(\log{n}) O(logn) 就是 O ( log ⁡ 2 n ) O(\log^2{n}) O(log2n)

时间复杂度: O ( n log ⁡ 2 n ) O(n\log^2{n}) O(nlog2n),期望得分: 30 p t s 30pts 30pts

算法五:线段树+堆

考虑在算法四的基础上加上 p u s h d o w n pushdown pushdown 操作;

在每个线段树节点都维护堆,父亲节点记录每次更改操作的值;

查询时就 p u s h d o w n pushdown pushdown 下来,删除时也直接在父亲节点的堆里面删除即可;

时间复杂度: O ( n log ⁡ 2 n ) O(n\log^2{n}) O(nlog2n),期望得分: 60 60 60~ 100 p t s 100pts 100pts

算法六:标记永久化

有了算法五 p u s h d o w n pushdown pushdown 操作的引入,可以想到标记永久化。

在下放修改操作时,用 log ⁡ n \log{n} logn 的复杂度递归下放,删除同理,加上深度复杂度 O ( log ⁡ 2 n ) O(\log^2{n}) O(log2n)

时间复杂度: O ( ( n + m ) log ⁡ 2 n ) O((n+m)\log^2{n}) O((n+m)log2n),期望得分: 100 p t s 100pts 100pts

code

#include<bits/stdc++.h>
#define N 200005
#define inf 0x3f3f3f3f
#define endl '\n'
#define lc (p<<1)
#define rc (p<<1|1)
#define debug cerr<<__LINE__<<endl
using namespace std;
int n,m,tmp;
struct node{
	int maxn;priority_queue<int>q;
	inline void init(){q.emplace(maxn=-1);}//堆和最大值直接初始为-1,查询时就不需要特判了
}t[N<<2];
inline char gc(){
	static const int L=1<<22|1;static char c[L],*a,*b;
	return (a==b)&&(b=(a=c)+fread(c,1,L,stdin),a==b)?-1:*a++;
}
inline int read(){
	register int f=1,k=0;
	register char c=gc();
	while(c!='-'&&(c<'0'||c>'9')) c=gc();
	if(c=='-') f=-1,c=gc();
	while(c>='0'&&c<='9') k=(k<<3)+(k<<1)+(c^48),c=gc();
	return f*k;
}
inline void write(register int x){
    if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar((x%10)|48);
}
inline void build(const int p,const int l,const int r){
	t[p].init();if(l==r) return;
	const register int mid=((l+r)>>1);
	build(lc,l,mid);build(rc,mid+1,r);
}
inline void pushnow(const int p,const int k){t[p].q.emplace(k),t[p].maxn=max(t[p].maxn,k);}//修改当前节点
inline void pushup(const int p){t[p].maxn=max(max(t[lc].maxn,t[rc].maxn),t[p].q.top());}//向上传递最大值
inline void pushdown(const int p,const int l,const int r,const int x,const int y,const int k){
	if(x<=l&&r<=y) return;//只修改覆盖范围在x到y之外的节点
	const register int mid=((l+r)>>1);
	if(x>mid) pushnow(lc,k),pushdown(rc,mid+1,r,x,y,k);//若当前区间覆盖范围在目标区间左侧,那么修改左区间,继续向右区间搜索
	else if(y<=mid) pushnow(rc,k),pushdown(lc,l,mid,x,y,k);//同上
	else pushdown(lc,l,mid,x,y,k),pushdown(rc,mid+1,r,x,y,k);//两边都有就都搜
	return pushup(p);//回溯
}
inline void update(const int p,const int l,const int r,const int x,const int y,const int k){
	if(x<=l&&r<=y) return pushnow(p,k);//找到合法区间就改
	const register int mid=((l+r)>>1);
	if(x<=mid) update(lc,l,mid,x,y,k);
	if(y>mid) update(rc,mid+1,r,x,y,k);
	return pushup(p);
}
inline void delet(const int p,const int l,const int r,const int x,const int y,const int k){
	if(x<=l&&r<=y) if(t[p].maxn<k) return;//剪去无用搜索
	if(t[p].q.top()==k){//要删除的元素在这个区间内
		t[p].q.pop();//更新
		pushdown(p,l,r,x,y,k);//在这个区间内继续搜
		return l==r?void(t[p].maxn=t[p].q.top()):pushup(p);//更新最底部的点,回溯
	}
	const register int mid=((l+r)>>1);
	if(x<=mid) delet(lc,l,mid,x,y,k);
	if(y>mid) delet(rc,mid+1,r,x,y,k);
	return pushup(p);
}
inline int query(const int p,const int l,const int r,const int x,const int y){
	if(x<=l&&r<=y) return t[p].maxn;
	const register int mid=((l+r)>>1);
	register int ans=t[p].q.top();
	if(x<=mid) ans=max(ans,query(lc,l,mid,x,y));
	if(y>mid) ans=max(ans,query(rc,mid+1,r,x,y));
	return ans;
}
main(void){
    n=read();m=read();
	t[0].init();build(1,1,n);
	while(m--){
		const register int op=read(),x=read(),y=read();
		if(op==1) update(1,1,n,x,y,read());
		else if(op==2) ~(tmp=query(1,1,n,x,y))?delet(1,1,n,x,y,tmp):void();
		else if(op==3) write(query(1,1,n,x,y)),putchar('\n');
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值