题解--苦涩
如果数据水,敬请谅解,也希望提供强数据。
如果有时间复杂度更优秀的做法,请写题解,我会感激的。
先看部分分吧。
10pts
每个点开一个堆,然后就是常规操作了。
20pts
只有添加,没有删除,相当于区间取 max,区间 max。那就意味着我们可以用线段树维护。
40pts
发现单点删除最大值用线段树不太好操作,也不好进行标记下放和信息。上传我们考虑暴力一点,用分块维护信息,每个块开一个堆代表整块都被插入的东西,每个点也要开一个堆,然后开一个 maxx 数组代表块内最大值。
插入的时候我们边角暴力,中间插入整块的堆,然后更新 maxx。
删除的时候我们比较它自己的堆和它所属的块的堆。如果他自己的堆的堆顶更大,则直接删除,然后更新 maxx。如果它所属的堆更大,则删除掉之后,把它塞入块内其余点的堆里,因为它们还存在这个值,所以不用更新 maxx。
查询的时候我们边角暴力询问单点堆顶和整块堆顶,中间的整块直接查询 maxx,就解决了。
时间复杂度在块长为
60pts(1)
既然要 polylog,首先堆的一个 log 很难避免,我们剩下的那个 log 就可以往线段树上想。
考虑使用标记永久化,这个永久化的标记形成了一个堆。我们再在线段树里维护一个 maxx,代表这个区间的最大值。这样,插入和查询操作就很好处理了。
现在我们考虑删除,由于是单点,首先我们找到这个最大值,然后在线段树里找到这个标记。我们需要把这个标记移除,并将其加入这个区间的其它点内,你可以再写一个 pushdown,也可以直接跑两遍插入(这样常数要大一点)。
时间复杂度
我终于知道 lxl 为什么直接拒了。
60pts(2)
如果你的分块可以支持区间修改,则可以通过 60 分的数据。
100pts
我们考虑怎么优化 60 分的线段树,使它支持区间修改。
我们可以想到先找到这一段的最大值是哪一个,然后把标记从这个区间上面移除。这样做看起来时间复杂度是不对的,但我们可以进行剪枝。如果这一个区间的最大值比我们需要删去的小,我们就不去了。
乍一看复杂度还是不对,但我们可以分析一下。我们时间复杂度相当于对于每一个最大值的连续区间,把它在线段树上分成不超过 log 个区间。由于我们插入的时候是整段一起插入同一个值,所以插入均摊下来会产生
我们每删除一个产生的线段树区间,最多花
代码十分好写,不到 2k。如果哪里没叙述清楚,请向我提出。
#include <bits/stdc++.h>
#define lc id<<1
#define rc id<<1|1
using namespace std;
const int maxn=2e5+5;
struct tree {
int l,r,mid,maxx;
priority_queue<int> q;
} t[maxn*4];
int n,m;
void build(int id,int l,int r) {
t[id]=(tree) {
l,r,(l+r)/2,-1
};
t[id].q.push(-1);
if(l==r)return;
int mid=(l+r)/2;
build(lc,l,mid);
build(rc,mid+1,r);
}
void addtag(int id,int v) {
t[id].q.push(v);
t[id].maxx=max(t[id].maxx,v);
}
void pushup(int id) {
t[id].maxx=max(max(t[lc].maxx,t[rc].maxx),t[id].q.top());
}
void pushdown(int id,int l,int r,int k) {//把 k 这个标记往 l,r 之外区间插入
if(t[id].l>=l&&t[id].r<=r) return;
if(l>t[id].mid)addtag(lc,k),pushdown(rc,l,r,k);
else if(r<=t[id].mid)addtag(rc,k),pushdown(lc,l,r,k);
else pushdown(lc,l,r,k),pushdown(rc,l,r,k);
pushup(id);
}
void add(int id,int l,int r,int v) {
if(t[id].l>=l&&t[id].r<=r) {
addtag(id,v);
return;
}
if(l<=t[id].mid)add(lc,l,r,v);
if(r>t[id].mid)add(rc,l,r,v);
pushup(id);
}
void del(int id,int l,int r,int k) {
if(t[id].l>=l&&t[id].r<=r&&t[id].maxx<k)return;//核心:剪枝
if(t[id].q.top()==k) {
t[id].q.pop();
pushdown(id,l,r,k);
if(t[id].l==t[id].r)t[id].maxx=t[id].q.top();
else pushup(id);
return;//由于只pop一个,这里弄完标记就走了
}
if(l<=t[id].mid)del(lc,l,r,k);
if(r>t[id].mid)del(rc,l,r,k);
pushup(id);
}
int ask(int id,int l,int r) {
if(t[id].l>=l&&t[id].r<=r) {
return t[id].maxx;
}
int ans=t[id].q.top();
if(l<=t[id].mid)ans=max(ans,ask(lc,l,r));
if(r>t[id].mid)ans=max(ans,ask(rc,l,r));
return ans;
}
int main() {
t[0].q.push(-1),t[0].maxx=-1;
int op,x,y,z;
scanf("%d%d",&n,&m);
build(1,1,n);
for(register int i=1; i<=m; i++) {
scanf("%d%d%d",&op,&x,&y);
switch(op) {
case 1: {
scanf("%d",&z);
add(1,x,y,z);
break;
}
case 2: {
z=ask(1,x,y);
if(z!=-1)del(1,x,y,z);
break;
}
case 3: {
printf("%d\n",ask(1,x,y));
break;
}
}
}
return 0;
}