[牛客周赛]未曾设想的道路

本文介绍了如何使用线段树结构,通过维护每个节点的前k最大值序列,并利用懒惰标记高效处理区间修改和查询操作,解决了区间内历史前k大数的问题。通过优先队列优化,单次操作时间复杂度为O(k log k log n),展示了高效的解决方案。
摘要由CSDN通过智能技术生成

未曾设想的道路

题解

线段树板子题

我们考虑建一棵线段树,每个点维护对应区间曾经出现过的历史前 k k k大数。
我们查询的时候就找到这个区间的前 k k k大值,输出第 k k k个。
显然,对于线段树上的每个点,我们需要一个维护前 k k k大的序列,且维护的序列得是有序的。
很明显,在维护序列有序的前提下加入一个数是 O ( k ) O\left(k\right) O(k)的,合并两个数列也是 O ( k ) O\left(k\right) O(k)的。
询问就将所有的询问区间的前 k k k大值的序列合并起来找第 k k k大即可。

但我们加上修改的操作该怎么办呢?
单纯的修改区间很简单,我们可以通过对修改的区间加上懒标记来进行维护。
但很明显,由于要维护下传的区间的历史的前 k k k大,我们不能直接加懒标记,因为 x x x有正有负,但我们可以考虑记录下历史前 k k k大的懒标记值。
同时,我们显然也需要知道当前区间中下传懒标记的前 k k k大数。
如果直接把这 k k k个一一配对时间复杂度会达到 O ( k 2 ) O\left(k^2\right) O(k2),也是不可取的。
我们可以先加两者第一大的两个数加进去,明显,接下来的可能最大值就是懒标记最大加区间第二大或懒标记第二大加区间最大,我们可以通过优先队列对其进行维护。
这样,下传懒标记的过程时间复杂度就是 O ( k l o g   k ) O\left(klog\,k\right) O(klogk)了。

而懒标记下传对下面序列懒标记的影响,我们只需要将下传的历史 k k k大懒标记序列加上下面一个点懒标记的值,再与其的历史 k k k大懒标记序列合并即可。
区间最大值直接加上懒标记就行了。

我们对应的询问与修改操作就这样解决了。
由于线段树上修改与查询操作会对应的区间有 l o g   n log\,n logn个,所以我们单次询问与修改操作的时间复杂度是 O ( k l o g   k l o g   n ) O\left(klog\,klog\,n\right) O(klogklogn)
总时间复杂度 O ( m k l o g   n   l o g   k + n k l o g   n ) O\left(mklog\,n\,log\,k+nklog\,n\right) O(mklognlogk+nklogn)

D e v i l Devil Devil魔鬼大佬有一种 O ( ( m + n ) k n ) O\left((m+n)k\sqrt{n}\right) O((m+n)kn )的分块做法,虽然看起来会 T T T但居然过了,而且比线段树跑得还快,是官方数据太水了吧。

源码

看起来思路很简单,但真的很难调。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int lim=10000000;
const int jzm=1e6+7;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<LL,LL> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
struct Array{
	int sta[105],stak;Array(){stak=0;}void clear(){stak=0;}
	void insert(int x){
		stak++;
		for(int i=1;i<=stak;i++)if(sta[i]<x||i==stak)
			{for(int j=stak-1;j>=i;j--)swap(sta[j],sta[j+1]);sta[i]=x;break;}
		if(stak>100)sta[stak--]=0;
	}
	void update(int aw){for(int i=1;i<=stak;i++)sta[i]+=aw;}
};
Array merge(Array x,Array y){
	Array res;int i=1,j=1;
	while(i<=x.stak&&j<=y.stak&&res.stak<100)
		if(x.sta[i]>y.sta[j])res.sta[++res.stak]=x.sta[i++];
		else res.sta[++res.stak]=y.sta[j++];
	while(i<=x.stak&&res.stak<100)res.sta[++res.stak]=x.sta[i++];
	while(j<=y.stak&&res.stak<100)res.sta[++res.stak]=y.sta[j++];
	return res;
}

int n,m,a[MAXN];
struct node{
	int x,y,val;node(){x=y=val=0;}node(int X,int Y,int V){x=X;y=Y;val=V;}
	bool friend operator < (const node &x,const node &y){return x.val<y.val;}
};
priority_queue<node>q;
struct ming{Array hislzy,hismx,mx;int lzy;};
class SegmenTree{
	private:
		ming tr[MAXN<<2];
		void pushup(int rt){
			tr[rt].hismx=merge(tr[rt<<1].hismx,tr[rt<<1|1].hismx);
			tr[rt].mx=merge(tr[rt<<1].mx,tr[rt<<1|1].mx);
		}
		void pushdown(int rt){
			if(tr[rt].hislzy.stak){
				while(!q.empty())q.pop();Array tmp;tmp.clear();
				q.push((node){1,1,tr[rt<<1].mx.sta[1]+tr[rt].hislzy.sta[1]});
				while(!q.empty()&&tmp.stak<100){
					node t=q.top();q.pop();tmp.sta[++tmp.stak]=t.val;
					if(t.x<tr[rt<<1].mx.stak&&t.y==1)q.push(node(t.x+1,1,tr[rt<<1].mx.sta[t.x+1]+tr[rt].hislzy.sta[1]));
					if(t.y<tr[rt].hislzy.stak)q.push(node(t.x,t.y+1,tr[rt<<1].mx.sta[t.x]+tr[rt].hislzy.sta[t.y+1]));
				}
				tr[rt<<1].hismx=merge(tmp,tr[rt<<1].hismx);tmp.clear();while(!q.empty())q.pop();
				q.push((node){1,1,tr[rt<<1|1].mx.sta[1]+tr[rt].hislzy.sta[1]});
				while(!q.empty()&&tmp.stak<100){
					node t=q.top();q.pop();tmp.sta[++tmp.stak]=t.val;
					if(t.x<tr[rt<<1|1].mx.stak&&t.y==1)q.push(node(t.x+1,1,tr[rt<<1|1].mx.sta[t.x+1]+tr[rt].hislzy.sta[1]));
					if(t.y<tr[rt].hislzy.stak)q.push(node(t.x,t.y+1,tr[rt<<1|1].mx.sta[t.x]+tr[rt].hislzy.sta[t.y+1]));
				}
				tr[rt<<1|1].hismx=merge(tmp,tr[rt<<1|1].hismx);tmp.clear();
				Array t=tr[rt].hislzy;t.update(tr[rt<<1].lzy);tr[rt<<1].hislzy=merge(tr[rt<<1].hislzy,t);
				t=tr[rt].hislzy;t.update(tr[rt<<1|1].lzy);tr[rt<<1|1].hislzy=merge(tr[rt<<1|1].hislzy,t);
				tr[rt<<1].mx.update(tr[rt].lzy);tr[rt<<1|1].mx.update(tr[rt].lzy);
				tr[rt<<1].lzy+=tr[rt].lzy;tr[rt<<1|1].lzy+=tr[rt].lzy;tr[rt].hislzy.clear();tr[rt].lzy=0;
			}
		}
	public:
		void build(int rt,int l,int r){
			if(l==r){tr[rt].hismx.insert(a[l]);tr[rt].mx.insert(a[l]);return ;}
			int mid=(l+r)>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);pushup(rt);
		}
		void modify(int rt,int l,int r,int al,int ar,int aw){
			if(l>r||l>ar||r<al||al>ar)return ;
			if(al<=l&&r<=ar){
				tr[rt].mx.update(aw);tr[rt].hismx=merge(tr[rt].hismx,tr[rt].mx);
				tr[rt].lzy+=aw;tr[rt].hislzy.insert(tr[rt].lzy);return ;
			}
			int mid=(l+r)>>1;pushdown(rt);
			if(al<=mid)modify(rt<<1,l,mid,al,ar,aw);
			if(ar>mid)modify(rt<<1|1,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		Array query(int rt,int l,int r,int al,int ar){
			Array res;res.clear();if(l>r||al>r||ar<l||al>ar)return res;
			if(al<=l&&r<=ar)return tr[rt].hismx;pushdown(rt);int mid=(l+r)>>1;
			if(ar<=mid)return query(rt<<1,l,mid,al,ar);
			if(al>mid)return query(rt<<1|1,mid+1,r,al,ar);
			return merge(query(rt<<1,l,mid,al,ar),query(rt<<1|1,mid+1,r,al,ar));
		}
}T;
signed main(){
	read(n);read(m);
	for(int i=1;i<=n;i++)read(a[i]);T.build(1,1,n);
	for(int i=1;i<=m;i++){
		int typ,l,r,k;read(typ);read(l);read(r);read(k);
		if(!typ)T.modify(1,1,n,l,r,k);
		else{
			Array res=T.query(1,1,n,l,r);
			k=min(res.stak,k);printf("%d\n",res.sta[k]);
		}
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值