【线段树】LOJ6576 线段树经典题

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/88993338

【前言】
雅礼集训的时候讲题人放出来的题,但没有原题,于是机房几个人写了拍了。
然后丢到了LOJ上。

【题目】
LOJ
你需要写一个数据结构维护长度为 n n 的三个序列 A , B , C A,B,C ,支持:

  • 对于 i [ l , r ] i\in[l,r] ,令 A i = min ( A i , x ) A_i=\min (A_i,x)
  • 对于 i [ l , r ] i\in [l,r] ,令 A i = A i + x A_i=A_i+x
  • i = l r A i \sum_{i=l}^r A_i
  • i = l r B i \sum_{i=l}^r B_i
  • max i = l r C i \max_{i=l}^r C_i

每次修改操作后(前两种),令 B i = B i + A i , C i = max ( C i , A i ) B_i=B_i+A_i,C_i=\max(C_i,A_i)

初始给定 A i A_i ,初始 B i = 0 , C i = A i B_i=0,C_i=A_i

n 2 × 1 0 5 n\leq 2\times 10^5

【解题思路】
这个东西细节很多,来写一写容易发生错误的地方吧,仅供参考,具体实现可以看代码。

维护信息

  • fmx,smx,cnt,len,sum \text{fmx,smx,cnt,len,sum}

以上是我维护的所有基本信息,分别代表:区间最大值/次大值,区间最大值个数,区间长度,区间和。

  • hmx1,hmx2,hsum1,hsum2,dhsum1,dhsum2,add1,add2,hadd1,hadd2 \text{hmx1,hmx2,hsum1,hsum2,dhsum1,dhsum2,add1,add2,hadd1,hadd2}​

以上是所有维护的最大值相关信息和非最大值相关信息,分别代表:区间历史最大值,区间历史版本和的差量和,区间历史版本和差量标记,区间加标记,区间历史最大加标记。

  • f1,f2 \text{f1,f2}

以上是维护这个区间最大值是否从左右儿子贡献上来。

为什么要维护上面这些信息?

对于操作 1 1 ,我们需要吉司机线段树将其转化为区间加,具体来说,若 f m x x fmx\leq x 我们可以直接跳过,若 s m x < v < f m x smx<v<fmx ,则相当于只对最大值进行加操作1,否则我们暴力递归下去更新。

而正是由于可能只对最大值进行操作,我们后面的所有标记都要分成最大值和非最大值两类来维护。

考虑操作 4 4 ,维护区间历史版本和,那么我们需要知道怎么对加法进行维护。假设在第 i i 个修改操作对一个区间加上了 x x ,那么在第 j j 个修改操作后进行询问时,第 i i 次操作对区间内历史版本和的贡献为 ( j i + 1 ) x = j x ( i 1 ) x (j-i+1)x=jx-(i-1)x ,于是我们可以对 hsum \text{hsum} 减去 l e n ( i 1 ) x len\cdot (i-1)x 。在进行操作 4 4 的时候,我们用 hsum \text{hsum} 加上 sum \text{sum} 乘上当前做的修改操作个数即可。同样我们还需要维护关于这个的标记进行下传。

考虑操作 5 5 维护区间历史版本最大值,我们需要维护历史最大值和历史最大加标记,后者用于下传。每次打加标记是,我们将历史最大加标记 hadd \text{hadd} 与当前标记 add \text{add} max \text{max} 即可。

由于要对最大值和非最大值分别应用标记,我们还需要维护最大值从左右儿子中哪个进行贡献(可能两个一起)。

pushdown

  • 所有下传标记时,应当判断最大值是否在左/右区间来确定应用的标记。

  • 对于 hadd \text{hadd} 的更新,应当是用当前节点上的 add \text{add} 标记和父亲的 hadd \text{hadd} 标记来更新,即

    t[ls].hadd1=max(t[ls].hadd1,t[ls].add1+(t[x].f1?t[x].hadd1:t[x].hadd2))

  • 对于 hmx \text{hmx} 的更新,应当使用当前区间 fmx,smx \text{fmx,smx} 与父亲的 hadd \text{hadd} 标记来更新,即

    t[ls].hmx1=max(t[ls].hmx1,t[ls].fmx+(t[x].f1?t[x].hadd1:t[x].hadd2));

  • 对于 sum,hsum \text{sum,hsum} 这类需要考虑应用区间长度的标记要格外注意

  • fmx,smx \text{fmx,smx} 的更新应当放在 hmx \text{hmx} 之后,否则应用是错误的。

pushup

  • 需要讨论最大值的来源于左右哪个孩子,进行对应贡献

build

  • 初始值叶子节点的关于 smx,hmx2 \text{smx,hmx2} 可以设为极小值。

upcover

  • 仅贡献最大值的时候,对于 hsum1,dhsum1 \text{hsum1,dhsum1} 的贡献要注意符号

update

  • 注意 hsum,dhsum \text{hsum,dhsum} 贡献的符号以及贡献的区间长度(最大值/非最大值个数)
  • 对于 smx \text{smx} ,如果它本身为极小值则不需要更新。

querys

  • 正常写注意初值就行了

最后复杂度就是 O ( q log n ) O(q\log n) ,常数巨大。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll ind,a[N];

namespace IO
{
	ll read()
	{
		ll ret=0,f=1;char c=getchar();
		while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return f?ret:-ret;
	}
	void write(ll x){if(x<0)putchar('-'),x=-x;if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Data_Structure
{
	struct Segment
	{
		#define lc (x<<1)
		#define rc (x<<1|1)
		struct node
		{
			ll fmx,smx,cnt,sum,len;
			ll hmx1,hmx2,add1,add2,hadd1,hadd2,hsum1,hsum2,dhsum1,dhsum2;
			bool f1,f2;
		}t[N<<2];
		void pushdown(int x)
		{
			int ls=x<<1,rs=ls|1;
			t[ls].hadd1=max(t[ls].hadd1,t[ls].add1+(t[x].f1?t[x].hadd1:t[x].hadd2));
			t[ls].hadd2=max(t[ls].hadd2,t[ls].add2+t[x].hadd2);
			t[ls].add1+=t[x].f1?t[x].add1:t[x].add2; 
			t[ls].add2+=t[x].add2;
			t[ls].hmx1=max(t[ls].hmx1,t[ls].fmx+(t[x].f1?t[x].hadd1:t[x].hadd2));
			t[ls].hmx2=max(t[ls].hmx2,t[ls].smx+t[x].hadd2);
			t[ls].sum+=(t[x].f1?t[x].add1:t[x].add2)*t[ls].cnt+t[x].add2*(t[ls].len-t[ls].cnt);
			t[ls].dhsum1+=t[x].f1?t[x].dhsum1:t[x].dhsum2;
			t[ls].dhsum2+=t[x].dhsum2;
			t[ls].hsum1+=(t[x].f1?t[x].dhsum1:t[x].dhsum2)*t[ls].cnt;
			t[ls].hsum2+=t[x].dhsum2*(t[ls].len-t[ls].cnt);
			t[ls].fmx+=t[x].f1?t[x].add1:t[x].add2;t[ls].smx+=t[x].add2;
			
			t[rs].hadd1=max(t[rs].hadd1,t[rs].add1+(t[x].f2?t[x].hadd1:t[x].hadd2));
			t[rs].hadd2=max(t[rs].hadd2,t[rs].add2+t[x].hadd2);
			t[rs].add1+=t[x].f2?t[x].add1:t[x].add2; 
			t[rs].add2+=t[x].add2;
			t[rs].hmx1=max(t[rs].hmx1,t[rs].fmx+(t[x].f2?t[x].hadd1:t[x].hadd2));
			t[rs].hmx2=max(t[rs].hmx2,t[rs].smx+t[x].hadd2);
			t[rs].sum+=(t[x].f2?t[x].add1:t[x].add2)*t[rs].cnt+t[x].add2*(t[rs].len-t[rs].cnt);
			t[rs].dhsum1+=t[x].f2?t[x].dhsum1:t[x].dhsum2;
			t[rs].dhsum2+=t[x].dhsum2;
			t[rs].hsum1+=(t[x].f2?t[x].dhsum1:t[x].dhsum2)*t[rs].cnt;
			t[rs].hsum2+=t[x].dhsum2*(t[rs].len-t[rs].cnt);
			t[rs].fmx+=t[x].f2?t[x].add1:t[x].add2;t[rs].smx+=t[x].add2;

			t[x].add1=t[x].add2=t[x].hadd1=t[x].hadd2=t[x].dhsum1=t[x].dhsum2=0;
		}
		void pushup(int x)
		{
			int ls=x<<1,rs=ls|1;
			if(t[ls].fmx==t[rs].fmx)
			{
				t[x].f1=t[x].f2=1;
				t[x].fmx=t[ls].fmx; t[x].smx=max(t[ls].smx,t[rs].smx); t[x].cnt=t[ls].cnt+t[rs].cnt;
				t[x].hmx1=max(t[ls].hmx1,t[rs].hmx1); t[x].hmx2=max(t[ls].hmx2,t[rs].hmx2);
				t[x].hsum1=t[ls].hsum1+t[rs].hsum1; t[x].hsum2=t[ls].hsum2+t[rs].hsum2;
				t[x].sum=t[ls].sum+t[rs].sum;
			}
			else 
			{
				t[x].f1=t[x].f2=0;
				if(t[ls].fmx<t[rs].fmx) swap(ls,rs),t[x].f2=1; else t[x].f1=1;
				t[x].fmx=t[ls].fmx; t[x].smx=max(t[ls].smx,t[rs].fmx); t[x].cnt=t[ls].cnt;
				t[x].hmx1=t[ls].hmx1; t[x].hmx2=max(t[ls].hmx2,max(t[rs].hmx1,t[rs].hmx2));
				t[x].hsum1=t[ls].hsum1; t[x].hsum2=t[ls].hsum2+t[rs].hsum1+t[rs].hsum2;
				t[x].sum=t[ls].sum+t[rs].sum;
			}
		}
		void build(int x,int l,int r)
		{
			t[x].len=r-l+1;
			if(l==r)
			{
				t[x].fmx=t[x].sum=t[x].hmx1=a[l];
				t[x].smx=t[x].hmx2=-inf;t[x].cnt=1;
				return;
			}
			int mid=(l+r)>>1;
			build(lc,l,mid);build(rc,mid+1,r);
			pushup(x);
		}
		void upcover(int x,int l,int r,int L,int R,ll v)
		{
			int mid=(l+r)>>1;
			if(L<=l && r<=R)
			{
				if(t[x].fmx<=v) return;
				if(t[x].smx<v) 
				{
					t[x].add1+=v-t[x].fmx; t[x].hadd1=max(t[x].hadd1,t[x].add1);
					t[x].hsum1+=-t[x].cnt*(v-t[x].fmx)*(ind-1);
					t[x].dhsum1+=-(v-t[x].fmx)*(ind-1); t[x].sum+=t[x].cnt*(v-t[x].fmx);
					t[x].fmx=v;
				} 
				else pushdown(x),upcover(lc,l,mid,L,R,v),upcover(rc,mid+1,r,L,R,v),pushup(x);
				return;
			}
			pushdown(x);
			if(L<=mid) upcover(lc,l,mid,L,R,v);
			if(R>mid) upcover(rc,mid+1,r,L,R,v);
			pushup(x);
		}
		void update(int x,int l,int r,int L,int R,ll v)
		{
			if(L<=l && r<=R)
			{
				t[x].add1+=v;t[x].add2+=v;
				t[x].hsum1+=-t[x].cnt*v*(ind-1); t[x].hsum2+=-(t[x].len-t[x].cnt)*v*(ind-1);
				t[x].dhsum1+=-v*(ind-1); t[x].dhsum2+=-v*(ind-1);
				t[x].sum+=t[x].len*v;
				t[x].fmx+=v;
				if(t[x].smx>-inf) t[x].smx+=v;
				t[x].hmx1=max(t[x].hmx1,t[x].fmx); t[x].hmx2=max(t[x].hmx2,t[x].smx);
				t[x].hadd1=max(t[x].hadd1,t[x].add1); t[x].hadd2=max(t[x].hadd2,t[x].add2);
				return;
			}
			pushdown(x);
			int mid=(l+r)>>1;
			if(L<=mid) update(lc,l,mid,L,R,v);
			if(R>mid) update(rc,mid+1,r,L,R,v);
			pushup(x);
			//printf("%d %d %lld %lld\n",l,r,t[x].dhsum1,t[x].dhsum2);
		}
		ll query1(int x,int l,int r,int L,int R)
		{
			if(L<=l && r<=R) return t[x].sum;
			pushdown(x);
			int mid=(l+r)>>1;ll res=0;
			if(L<=mid) res+=query1(lc,l,mid,L,R);
			if(R>mid) res+=query1(rc,mid+1,r,L,R);
			return res;
		}
		ll query2(int x,int l,int r,int L,int R)
		{
			//printf("%d %d %lld %lld %lld %lld %lld\n",l,r,t[x].sum,t[x].hsum1,t[x].hsum2,t[x].dhsum1,t[x].dhsum2);
			if(L<=l && r<=R) return t[x].sum*ind+t[x].hsum1+t[x].hsum2;
			pushdown(x);
			int mid=(l+r)>>1;ll res=0;
			if(L<=mid) res+=query2(lc,l,mid,L,R);
			if(R>mid) res+=query2(rc,mid+1,r,L,R);
			return res;
		}
		ll query3(int x,int l,int r,int L,int R)
		{
			if(L<=l && r<=R) return max(t[x].hmx1,t[x].hmx2);
			pushdown(x);
			int mid=(l+r)>>1;ll res=-inf;
			if(L<=mid) res=max(res,query3(lc,l,mid,L,R));
			if(R>mid) res=max(res,query3(rc,mid+1,r,L,R));
			return res;
		}
		void debug(int x,int l,int r)
		{
			printf("%d %d:%lld %lld %lld %lld %lld %lld %lld\n",l,r,t[x].fmx,t[x].smx,t[x].sum,t[x].hsum1,t[x].hsum2,t[x].dhsum1,t[x].dhsum2);
			if(l==r) return;
			pushdown(x);
			int mid=(l+r)>>1;
			debug(lc,l,mid);debug(rc,mid+1,r);
		}
		#undef lc
		#undef rc
	}T;
}
using namespace Data_Structure;

namespace DreamLolita
{
	int n,Q;
	void solution()
	{
		n=read();Q=read();
		for(int i=1;i<=n;++i) a[i]=read();
		T.build(1,1,n);
		for(int i=1;i<=Q;++i)
		{
			int op=read(),l=read(),r=read();
			if(op==1) ++ind,T.upcover(1,1,n,l,r,read());
			else if(op==2) ++ind,T.update(1,1,n,l,r,read());
			else if(op==3) writeln(T.query1(1,1,n,l,r));
			else if(op==4) writeln(T.query2(1,1,n,l,r));
			else writeln(T.query3(1,1,n,l,r));
			//T.debug(1,1,n);puts("");
		}
	}
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	DreamLolita::solution();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/88993338
今日推荐