吉司机线段树学习笔记

发布时间 2023-08-26 16:28:50作者: 傻阙的缺

给出一个长度为n的数列A同时定义一个辅助数组 B,B开始与 A完全相同。接下来进行了m次操作,构造一个数据结构维护以下五类操作:

  1. 对于所有i\(\in\)[l,r],将\(A_i\)加上k

  2. 对于所有i\(\in\)[l,r],将\(A_i\)min(\(A_i\),v)

  3. \(\sum\limits_{i=l}^rA_i\)

  4. 对于所有i\(\in\)[l,r],求\(A_i\)的最大值

  5. 对于所有i\(\in\)[l,r],求\(B_i\)的最大值

其中,在每一次操作后,我们都进行一次更新,让\(B_i\leftarrow\max(m,n)\)

对于操作3,4我们非常熟悉,只需要在线段树中维护sum和max即可。

对于操作1同样的,只需要用懒标记tag1标记即可

对于操作5需要维护历史最大值,所以在线段树中我们加上一个变量叫做lszd即一段区间内的历史最大值,然后用懒标记tag2记录修改中最大的修改值即可(eg:在1~5中依次增加2 -5 3,则tag2会记录为2,下传也会下传2)

最难的是操作2,因为线段树是没有取最小值的操作。那么我们可以用一种非常牛逼的操作:就是将线段树维护的max改成fi和se,维护的是一段区间内的第一大的数和第二大的数(为什么要这么做待会再说).那么对于一段要修改的数:我们就可以分类讨论了:

1. 当v>=fi时,直接return.
2. 当se<v<fi时,我们直接让第一大的数加上(v-fi),然后打上懒标记(怎么打待会再说).
3. 当v<=se时,我们继续往下递归,之后再pushup,更新该区间内的sum,fi,se等.

那么应该解决一些问题:

第一个:怎么打懒标记?

解答:既然我们在操作二时已经将一个数分成第一大和其他的数,那么我们的懒标记也要区别对待,对待第一大的其他的数。回想一下,我们要维护两个值:修改的值和修改中的最大的修改值,区别对待后:(第1个)最大值需要修改值,(第2个)最大值修改中的最大的修改值,(第3个)其他的值需要修改值,(第4个)其他的值修改中的最大的修改值

第二个:操作2的时间复杂度?

解答:提供一种(m+n) log n 的做法,看似只要v够小,就会一直执行上面的第三种情况,时间就会退化成n log n,但第三种情况执行完后,fi和se必然会合并,下一次就不会再这样,而修改m次,每一次最多使log n个数改变,故时间复杂度为(m+n) log n的时间复杂度.

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e5+50,INF=1e15;
ll n,m,a[N];
struct jgt
{
	ll sum,fi,se,lszd,cnt;
	ll tag1,tag2,tag3,tag4;
}tr[N*8];
void pushup(ll wz)
{
	tr[wz].sum=tr[wz*2].sum+tr[wz*2+1].sum;
	tr[wz].fi=max(tr[wz*2].fi,tr[wz*2+1].fi);
	tr[wz].lszd=max(tr[wz*2].lszd,tr[wz*2+1].lszd);
	if(tr[wz*2].fi==tr[wz*2+1].fi)
	{
		tr[wz].se=max(tr[wz*2].se,tr[wz*2+1].se);
		tr[wz].cnt=tr[wz*2].cnt+tr[wz*2+1].cnt;
	}
	else if(tr[wz*2].fi>tr[wz*2+1].fi)
	{
		tr[wz].se=max(tr[wz*2].se,tr[wz*2+1].fi);
		tr[wz].cnt=tr[wz*2].cnt;
	}
	else
	{
		tr[wz].se=max(tr[wz*2].fi,tr[wz*2+1].se);
		tr[wz].cnt=tr[wz*2+1].cnt;
	}
	return ;
}
void pushdown(ll wz,ll l,ll r)
{
	ll maxn=max(tr[wz*2].fi,tr[wz*2+1].fi);
	ll mid=(l+r)/2;
	if(tr[wz*2].fi==maxn)
	{
		tr[wz*2].sum=tr[wz*2].sum+tr[wz].tag1*tr[wz*2].cnt+(mid-l+1-tr[wz*2].cnt)*tr[wz].tag3;
		tr[wz*2].lszd=max(tr[wz*2].lszd,tr[wz*2].fi+tr[wz].tag2);
		tr[wz*2].fi+=tr[wz].tag1;
		if(tr[wz*2].se!=-INF) tr[wz*2].se+=tr[wz].tag3;
		tr[wz*2].tag2=max(tr[wz*2].tag2,tr[wz].tag2+tr[wz*2].tag1);
		tr[wz*2].tag4=max(tr[wz*2].tag4,tr[wz].tag4+tr[wz*2].tag3);
		tr[wz*2].tag1+=tr[wz].tag1;
		tr[wz*2].tag3+=tr[wz].tag3; 
	}
	else
	{
		tr[wz*2].sum=tr[wz*2].sum+(mid-l+1)*tr[wz].tag3;
		tr[wz*2].lszd=max(tr[wz*2].lszd,tr[wz*2].fi+tr[wz].tag4);
		tr[wz*2].fi+=tr[wz].tag3;
		if(tr[wz*2].se!=-INF) tr[wz*2].se+=tr[wz].tag3;
		tr[wz*2].tag2=max(tr[wz*2].tag2,tr[wz].tag4+tr[wz*2].tag1);
		tr[wz*2].tag4=max(tr[wz*2].tag4,tr[wz].tag4+tr[wz*2].tag3);
		tr[wz*2].tag1+=tr[wz].tag3;
		tr[wz*2].tag3+=tr[wz].tag3; 
	}
	if(tr[wz*2+1].fi==maxn)
	{
		tr[wz*2+1].sum=tr[wz*2+1].sum+tr[wz].tag1*tr[wz*2+1].cnt+(r-mid-tr[wz*2+1].cnt)*tr[wz].tag3;
		tr[wz*2+1].lszd=max(tr[wz*2+1].lszd,tr[wz*2+1].fi+tr[wz].tag2);
		tr[wz*2+1].fi+=tr[wz].tag1;
		if(tr[wz*2+1].se!=-INF) tr[wz*2+1].se+=tr[wz].tag3;
		tr[wz*2+1].tag2=max(tr[wz*2+1].tag2,tr[wz].tag2+tr[wz*2+1].tag1);
		tr[wz*2+1].tag4=max(tr[wz*2+1].tag4,tr[wz].tag4+tr[wz*2+1].tag3);
		tr[wz*2+1].tag1+=tr[wz].tag1;
		tr[wz*2+1].tag3+=tr[wz].tag3; 
	}
	else
	{
		tr[wz*2+1].sum=tr[wz*2+1].sum+(r-mid)*tr[wz].tag3;
		tr[wz*2+1].lszd=max(tr[wz*2+1].lszd,tr[wz*2+1].fi+tr[wz].tag4);
		tr[wz*2+1].fi+=tr[wz].tag3;
		if(tr[wz*2+1].se!=-INF) tr[wz*2+1].se+=tr[wz].tag3;
		tr[wz*2+1].tag2=max(tr[wz*2+1].tag2,tr[wz].tag4+tr[wz*2+1].tag1);
		tr[wz*2+1].tag4=max(tr[wz*2+1].tag4,tr[wz].tag4+tr[wz*2+1].tag3);
		tr[wz*2+1].tag1+=tr[wz].tag3;
		tr[wz*2+1].tag3+=tr[wz].tag3; 
	}
	tr[wz].tag1=0;
	tr[wz].tag2=0;
	tr[wz].tag3=0;
	tr[wz].tag4=0;
	return ;
}
void BT(ll wz,ll l,ll r)
{
	if(l==r)
	{
		tr[wz].sum=a[l];
		tr[wz].fi=a[l];
		tr[wz].se=-INF;
		tr[wz].lszd=a[l];
		tr[wz].cnt=1;
		return ;
	}
	ll mid=(l+r)/2;
	BT(wz*2,l,mid);
	BT(wz*2+1,mid+1,r);
	pushup(wz);
	return ;
}
void add(ll wz,ll l,ll r,ll le,ll ri,ll k)
{
	pushdown(wz,l,r);
	if(le<=l&&ri>=r)
	{
		tr[wz].sum=tr[wz].sum+(r-l+1)*k;
		tr[wz].fi+=k;
		if(tr[wz].se!=-INF) tr[wz].se+=k;
		tr[wz].lszd=max(tr[wz].lszd,tr[wz].fi);
		tr[wz].tag1+=k;
		tr[wz].tag2=max(tr[wz].tag2,tr[wz].tag1);
		tr[wz].tag3+=k;
		tr[wz].tag4=max(tr[wz].tag4,tr[wz].tag3);
		return ;
	}
	ll mid=(l+r)/2;
	if(le<=mid) add(wz*2,l,mid,le,ri,k);
	if(ri>mid) add(wz*2+1,mid+1,r,le,ri,k);
	pushup(wz);
	return ;
}
void gai(ll wz,ll l,ll r,ll le,ll ri,ll v)
{
	if(le<=l&&ri>=r)
	{
		if(v>=tr[wz].fi) return ;
		if(v>tr[wz].se)
		{
			tr[wz].sum=tr[wz].sum+tr[wz].cnt*(v-tr[wz].fi);
			tr[wz].tag1=tr[wz].tag1+v-tr[wz].fi;
			tr[wz].fi=v;
			tr[wz].tag2=max(tr[wz].tag2,tr[wz].tag1);
			return ;
		}
	}
	pushdown(wz,l,r);
	ll mid=(l+r)/2;
	if(le<=mid) gai(wz*2,l,mid,le,ri,v);
	if(ri>mid) gai(wz*2+1,mid+1,r,le,ri,v);
	pushup(wz);
	return ;
}
ll querysum(ll wz,ll l,ll r,ll le,ll ri)
{
	if(le<=l&&ri>=r) return tr[wz].sum;
	pushdown(wz,l,r);
	ll mid=(l+r)/2,zh=0;
	if(le<=mid) zh+=querysum(wz*2,l,mid,le,ri);
	if(ri>mid) zh+=querysum(wz*2+1,mid+1,r,le,ri);
	return zh;
}
ll queryfi(ll wz,ll l,ll r,ll le,ll ri)
{
	if(le<=l&&ri>=r) return tr[wz].fi;
	pushdown(wz,l,r);
	ll mid=(l+r)/2,zd=-INF;
	if(le<=mid) zd=max(zd,queryfi(wz*2,l,mid,le,ri));
	if(ri>mid) zd=max(zd,queryfi(wz*2+1,mid+1,r,le,ri));
	return zd;
}
ll querylszd(ll wz,ll l,ll r,ll le,ll ri)
{
	if(le<=l&&ri>=r) return tr[wz].lszd;
	pushdown(wz,l,r);
	ll mid=(l+r)/2,zd=-INF;
	if(le<=mid) zd=max(zd,querylszd(wz*2,l,mid,le,ri));
	if(ri>mid) zd=max(zd,querylszd(wz*2+1,mid+1,r,le,ri));
	return zd;
}
int main()
{
	scanf("%lld %lld",&n,&m);
	for(ll i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	BT(1,1,n);
	ll op,l,r,k,v;
	for(ll i=1;i<=m;i++)
	{
		scanf("%lld",&op);
		if(op==1)
		{
			scanf("%lld %lld %lld",&l,&r,&k);
			add(1,1,n,l,r,k);
		}
		else if(op==2)
		{
			scanf("%lld %lld %lld",&l,&r,&v);
			gai(1,1,n,l,r,v);
		}
		else if(op==3)
		{
			scanf("%lld %lld",&l,&r);
			printf("%lld\n",querysum(1,1,n,l,r));
		}
		else if(op==4)
		{
			scanf("%lld %lld",&l,&r);
			printf("%lld\n",queryfi(1,1,n,l,r));
		}
		else if(op==5)
		{
			scanf("%lld %lld",&l,&r);
			printf("%lld\n",querylszd(1,1,n,l,r));
		}
	}
	return 0;
}