P7476 「C.E.L.U-02」苦涩
题目大意:
有n个可重集合,初始为空;将执行 m m m 次操作;
操作有 3 3 3 种:
-
往 l l l 到 r r r 的集合中加入一个元素 k k k
-
删除 l l l 到 r r r 集合中最大的元素,若该集合内有多个最大元素,则只删除一个,若 l l l 到 r r r 集合都无元素,则无视
-
查询 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(nnlogn),期望得分: 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;
}