摸鱼之 - 吉老师线段树

【学习笔记】摸鱼之 - 吉老师线段树

  • 这是吉老师在 2016 2016 年国家集训队论文中提到的线段树处理历史区间最值的问题。

我们先看一道例题:

大致题面

  • 给你一个序列 A A ,要支持以下操作:
    • 0 l r val: 区间 [ l , r ] [l,r] v a l val 取最小值
    • 1 l r:询问 max i = l r \max_{i=l}^r
    • 2 l r:询问 i = l r A i \sum_{i=l}^rA_i

S o l \mathrm{Sol}

  • 我们就设 m x , m x 2 , c m x mx,mx2,cmx 分别为区间最大值,次大值,最大值的个数
  • 我们考虑区间取 m i n min 得过程,就是把 m x = v a l mx=val 的过程。那么我们分情况考虑:
    • 如果当 m x v a l mx\leq val 显然对原序列不会产生影响
    • 如果当 m x 2 v a l m x mx2\leq val\leq mx ,那么此时区间和 s u m sum 只要减去 c m x × ( m x v a l ) cmx\times (mx-val) 即可,这很有道理吧!
    • 否则我们就只能暴力向下做然后 p u s h u p pushup 啦。
  • 这个流程应该还是比较清楚的,根据什么势能分析可知复杂度为 O ( m log n ) O(m\log n) (我不会证)

然后就是一道众所周知的卡常题目

  • 我至今还没有卡过点 4 4 (正确性没问题,就是链比较黑了。。。

大致题面

S o l \mathrm{Sol}

  • 这是一道各种操作一起来的题目。
  • 我们像例题一样,设 m x , m x 2 , c m x mx,mx2,cmx 分别为区间最大值,次大值,最大值的个数,最小值同理。以及 s u m sum 区间和, t a g , t a g m x , t a g m i tag,tagmx,tagmi 一系列懒标记。
  • 然后就是像例题一样的大力 p u s h u p pushup
  • 重要的是那些最大,最小值修改(我们那修改最小值来说吧:当我们该区间最小值时还应该考虑去修改区间最大值,即如果 v a l t a g m x val\leq tagmx ,那么说有的数都要变成 v a l val 以及 t a g m x tagmx 也要变成 v a l val 。最大值修改同理。
  • 维护的时候,区间加优于区间最值问题。

C o d e \mathrm{Code}

  • 下面代码时会 T L E TLE 的,求那位神仙帮我卡卡常(我已经用了众所周知的卡常方法。
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;

char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}

inline int read()
{
	int sum=0,ff=1; char ch=gc();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=gc();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=gc();
	return sum*ff;
}

const int N=5e5+5;
const int INF=1e9;

int n,m,a[N];

struct Seg
{
	struct nood
	{
		int mx,mx2,mn,mn2;
		int cmx,cmn,tmx,tmn,tag;
		int sum;
	};
	nood T[N*4];
	inline int min(int i,int j)
	{
		if(i<j) return i;
		return j;
	}
	inline int max(int i,int j)
	{
		if(i>j) return i;
		return j;
	}
	inline void push_up(int rt)
	{
		int ls=rt<<1,rs=rt<<1|1;
		T[rt].sum=T[ls].sum+T[rs].sum;
		if(T[ls].mx==T[rs].mx)
		{
			T[rt].mx=T[ls].mx;
			T[rt].cmx=T[ls].cmx+T[rs].cmx;
			T[rt].mx2=max(T[ls].mx2,T[rs].mx2);
		}
		else if(T[ls].mx<T[rs].mx)
		{
			T[rt].mx=T[rs].mx;
			T[rt].cmx=T[rs].cmx;
			T[rt].mx2=max(T[rs].mx2,T[ls].mx);
		}
		else if(T[ls].mx>T[rs].mx)
		{
			T[rt].mx=T[ls].mx;
			T[rt].cmx=T[ls].cmx;
			T[rt].mx2=max(T[ls].mx2,T[rs].mx);
		}
		if(T[ls].mn==T[rs].mn)
		{
			T[rt].mn=T[ls].mn;
			T[rt].cmn=T[ls].cmn+T[rs].cmn;
			T[rt].mn2=min(T[ls].mn2,T[rs].mn2);
		}
		else if(T[ls].mn<T[rs].mn)
		{
			T[rt].mn=T[ls].mn;
			T[rt].cmn=T[ls].cmn;
			T[rt].mn2=min(T[ls].mn2,T[rs].mn);
		}
		else if(T[ls].mn>T[rs].mn)
		{
			T[rt].mn=T[rs].mn;
			T[rt].cmn=T[rs].cmn;
			T[rt].mn2=min(T[ls].mn,T[rs].mn2);
		}
	}
	inline void pd_add(int rt,int l,int r,int val)
	{
		T[rt].sum+=(r-l+1)*val;
		T[rt].mx+=val;
		T[rt].mn+=val;
		if(T[rt].mn2!=INF) T[rt].mn2+=val;
		if(T[rt].mx2!=-INF) T[rt].mx2+=val;
		if(T[rt].tmn!=INF) T[rt].tmn+=val;
		if(T[rt].tmx!=-INF) T[rt].tmx+=val;
		T[rt].tag+=val;
	}
	inline void pd_MAX(int rt,int val)
	{
		if(T[rt].mn>=val) return;
		T[rt].sum+=(val-T[rt].mn)*T[rt].cmn;
		if(T[rt].mx2==T[rt].mn) T[rt].mx2=val;
		if(T[rt].mx==T[rt].mn) T[rt].mx=val;
		if(T[rt].tmn<val) T[rt].tmn=val;
		T[rt].mn=val;
		T[rt].tmx=val;
	}
	inline void pd_MIN(int rt,int val)
	{
		if(T[rt].mx<=val) return;
		T[rt].sum+=(val-T[rt].mx)*T[rt].cmx;
		if(T[rt].mn2==T[rt].mx) T[rt].mn2=val;
		if(T[rt].mn==T[rt].mx) T[rt].mn=val;
		if(T[rt].tmx>val) T[rt].tmx=val;
		T[rt].mx=val;
		T[rt].tmn=val;
	}
	inline void pd(int rt,int l,int r)
	{
		int mid=(l+r)/2;
		if(T[rt].tag)
		{
			pd_add(rt<<1,l,mid,T[rt].tag);
			pd_add(rt<<1|1,mid+1,r,T[rt].tag);
		}
		if(T[rt].tmn!=INF)
		{
			pd_MIN(rt<<1,T[rt].tmn);
			pd_MIN(rt<<1|1,T[rt].tmn);
		}
		if(T[rt].tmx!=-INF)
		{
			pd_MAX(rt<<1,T[rt].tmx);
			pd_MAX(rt<<1|1,T[rt].tmx);
		}
		T[rt].tag=0,T[rt].tmn=INF,T[rt].tmx=-INF;
	}
	inline void build(int rt,int l,int r)
	{
		T[rt].tmx=-INF,T[rt].tmn=INF;
		if(l==r)
		{
			T[rt].sum=T[rt].mx=T[rt].mn=a[l];
			T[rt].mn2=INF,T[rt].mx2=-INF;
			T[rt].cmn=1,T[rt].cmx=1;
			return;
		}
		int mid=(l+r)/2;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		push_up(rt);
	}
	inline void add(int rt,int l,int r,int ll,int rr,int val)
	{
		if(rr<l||ll>r) return;
		if(ll<=l&&r<=rr)
		{
			pd_add(rt,l,r,val);
			return;
		}
		pd(rt,l,r);
		int mid=(l+r)/2;
		if(ll<=mid) add(rt<<1,l,mid,ll,rr,val);
		if(rr>mid) add(rt<<1|1,mid+1,r,ll,rr,val);
		push_up(rt);
	}
	inline void MIN(int rt,int l,int r,int ll,int rr,int val)
	{
		if(rr<l||ll>r) return;
		if(T[rt].mx<=val) return;
		if(ll<=l&&r<=rr&&T[rt].mx2<val)
		{
			pd_MIN(rt,val);
			return;
		}
		int mid=(l+r)/2;
		pd(rt,l,r);
		if(ll<=mid) MIN(rt<<1,l,mid,ll,rr,val);
		if(rr>mid) MIN(rt<<1|1,mid+1,r,ll,rr,val);
		push_up(rt);
	}
	inline void MAX(int rt,int l,int r,int ll,int rr,int val)
	{
		if(rr<l||ll>r) return;
		if(T[rt].mn>=val) return;
		if(ll<=l&&r<=rr&&T[rt].mn2>val)
		{
			pd_MAX(rt,val);
			return;
		}
		int mid=(l+r)/2;
		pd(rt,l,r);
		if(ll<=mid) MAX(rt<<1,l,mid,ll,rr,val);
		if(rr>mid) MAX(rt<<1|1,mid+1,r,ll,rr,val);
		push_up(rt);
	}
	inline long long ask_add(int rt,int l,int r,int ll,int rr)
	{
		if(rr<l||ll>r) return 0ll;
		if(ll<=l&&r<=rr) return T[rt].sum;
		int mid=(l+r)/2,sum=0;
		pd(rt,l,r);
		if(ll<=mid) sum+=ask_add(rt<<1,l,mid,ll,rr);
		if(rr>mid) sum+=ask_add(rt<<1|1,mid+1,r,ll,rr);
		return sum;
	}
	inline int ask_MAX(int rt,int l,int r,int ll,int rr)
	{
		if(rr<l||ll>r) return -INF;
		if(ll<=l&&r<=rr) return T[rt].mx;
		int mid=(l+r)/2,mx=-INF;
		pd(rt,l,r);
		if(ll<=mid) mx=max(mx,ask_MAX(rt<<1,l,mid,ll,rr));
		if(rr>mid) mx=max(mx,ask_MAX(rt<<1|1,mid+1,r,ll,rr));
		return mx;
	}
	inline int ask_MIN(int rt,int l,int r,int ll,int rr)
	{
		if(rr<l||ll>r) return INF;
		if(ll<=l&&r<=rr) return T[rt].mn;
		int mid=(l+r)/2,mn=INF;
		pd(rt,l,r);
		if(ll<=mid) mn=min(mn,ask_MIN(rt<<1,l,mid,ll,rr));
		if(rr>mid) mn=min(mn,ask_MIN(rt<<1|1,mid+1,r,ll,rr));
		return mn;
	}
};
Seg T;

signed main()
{
	n=read();
	for ( register int i=1;i<=n;i++ ) a[i]=read();
	T.build(1,1,n);
	m=read();
	for ( register int i=1;i<=n;++i )
	{
		int op,l,r,x;
		op=read(),l=read(),r=read();
		if(op<=3) x=read();
		if(op==1) T.add(1,1,n,l,r,x);
		if(op==2) T.MAX(1,1,n,l,r,x);
		if(op==3) T.MIN(1,1,n,l,r,x);
		if(op==4) printf("%lld\n",T.ask_add(1,1,n,l,r));
		if(op==5) printf("%lld\n",T.ask_MAX(1,1,n,l,r));
		if(op==6) printf("%lld\n",T.ask_MIN(1,1,n,l,r));
	}
	return 0;
}

历史最值问题(先鸽了。。

猜你喜欢

转载自blog.csdn.net/wangyiyang2/article/details/105419520
今日推荐