• 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;
    }
    /*
    
    */
    
  • 相关阅读:
    【CF1015D】Walking Between Houses(构造,贪心)
    【CF1068D】Array Without Local Maximums(计数DP)
    【CF1068C】Colored Rooks(构造)
    172.处理机控制与杂项指令
    171.控制转移指令
    170.串处理指令
    169.逻辑指令
    168.算术指令
    Volume 1. Big Number(uva)
    Volume 1. String(uva)
  • 原文地址:https://www.cnblogs.com/Anchor1201/p/16525418.html
Copyright © 2020-2023  润新知