P6717 [CCO2018] Boring Lectures题解

提要

看别人做的题找到的,联系一下最近做的两道CF和模拟赛题很自然地就会了。

一种不需要线段树分治的在线做法,但时空复杂度相对略高。

提示

  • 一个显然的想法是考虑把“查询长度为 \(K\) 的区间的最大值+次大值”变成“修改长度为 \(K\) 的区间”。

  • 如果所有插入操作和所有删除操作修改的区间相同,也就是只存在“操作A”和“删除操作A”的话,如果此时线段树下传标记不好维护,可以考虑标记永久化。

  • 对顶堆是一种支持插入/删除某个值,查询最值的 $\log $ 数据结构,通过使用一般的堆来实现。

题解

维护这样一个线段树,维护的单点信息是:位置 \(i\) 表示 \([i,i+K)\) 区间的最大值和最大值+次大值的相关信息。

那么维护的区间信息是当前区间的 最大值 和 最大的(最大值+次大值) 值。

接下来把替换看成 区间删除/加入 操作,最初建树看成插入操作。

考虑区间修改需要维护一个区间插入标记,使用标记永久化的技术做,这里标记可以采用优先队列的方式存储,也就是节点维护了当前一整个区间都会被插入的一些数的集合,我们在每个节点用一个堆把这些值存起来。

这样对于删除操作也是容易的,因为插入时递归到的区间和删除时一模一样,所以把维护的堆换成支持删除的对顶堆即可。

线段树的信息上传是细节问题,不再赘述,不理解可以见代码。

查询答案即直接调用根节点信息。

时间复杂度 \(\mathcal O(n\log^2 n+q\log^2 n)\),空间复杂度 \(\mathcal O(n\log n)\)

优化

可以把用于维护标记的优先队列换成配对堆等可以 \(\mathcal O(1)\) 插入,\(\mathcal O(\log n)\) 查询的堆,再稍微修改一下建树的过程就能做到 \(\mathcal O(n\log n+q\log ^2 n )\),空间复杂度不变。

这样做成功优化时间的本质是:只有删除才会导致用到堆的查询,而如果是只有插入可以直接凭借维护最大值就维护出答案(这部分就是“稍微修改建树过程”的优化)。

于是进一步地可以知道,如果所有修改单调递增,那么时间复杂度为 \(\mathcal O(n\log n+q\log n)\)

代码

未进行优化的版本,在洛谷上开 O2 可过,实际码量很小,1.16kb。

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template<typename T, typename ...Args>
inline void read(T &x, Args &...args){read(x),read(args...);}
template<typename T>
inline void write(T x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
#define pc putchar
const int N=2e6+5,M=1e6+5,INF=1e9+7;
int n,m,K,q,a[N];
struct SGT{
	priority_queue<int>ins,del;
	int Maxs,Max;
	int Gettop(){while(!ins.empty()&&!del.empty()&&ins.top()==del.top()) ins.pop(),del.pop();return ins.empty()?0:ins.top();}
}t[N<<2];
void Pushup(int x){
	int tmp=t[x].Gettop();
	t[x].del.push(tmp);
	int tmp1=t[x].Gettop();
	t[x].ins.push(tmp);
	
	t[x].Maxs=max(t[x<<1].Maxs,t[x<<1|1].Maxs);
	t[x].Maxs=max(t[x].Maxs,max({t[x<<1].Max,t[x<<1|1].Max,tmp1})+tmp);
	
	t[x].Max=max({t[x<<1].Max,t[x<<1|1].Max,tmp});
	return ;
}
void Modify(int x,int l,int r,int ql,int qr,int v,int tp){
	if(ql<=l&&r<=qr){
		if(tp) t[x].ins.push(v);
		else t[x].del.push(v);
		Pushup(x);
		return ;
	}
	int mid=l+r>>1;
	if(ql<=mid) Modify(x<<1,l,mid,ql,qr,v,tp);
	if(qr>mid) Modify(x<<1|1,mid+1,r,ql,qr,v,tp);
	Pushup(x);
	return ;
}
inline void _Solve(){
	read(n,K,q);
	for(int i=1;i<=n;i++) read(a[i]);
	m=n-K+1;
	for(int i=1;i<=n;i++) Modify(1,1,m,max(i-K+1,1),min(i,m),a[i],1);
	write(t[1].Maxs),pc('\n');
	while(q--){
		int pos,v;
		read(pos,v);
		Modify(1,1,m,max(pos-K+1,1),min(pos,m),a[pos],0);
		a[pos]=v;
		Modify(1,1,m,max(pos-K+1,1),min(pos,m),a[pos],1);
		write(t[1].Maxs),pc('\n');
	}
	return ;
}
signed main(){
	int _T=1;while(_T--) _Solve();
	return 0;
}
/*

*/
posted @ 2022-07-27 16:43  __Anchor  阅读(52)  评论(0编辑  收藏  举报