题解:[ONTAK2015] Stumilowy sad

题目传送门

如果你不会 Segment Tree Beats/吉司机线段树

题意分析

区间操作,想到线段树是显然的,对于四个操作:

  1. \([l,r]\) 增加 \(x\)

  2. \([l,r]\)\(x\)\(\min\),记为 \(\operatorname{chkmin}\)

  3. 这个有一点意思。

    一开始以为是区间覆盖,但是一个区域实际上可以种多棵树。然而,一个区域内的所有树,只有最高的那一棵对于答案有影响。

    因此我们也仅仅需要维护最高的那一棵,这个操作也就等价于给 \([l,r]\)\(x\)\(\max\),记为 \(\operatorname{chkmax}\)

  4. 查询 \([l,r]\) 的最大值。

区间最值操作,显然可以用吉司机线段树维护。

时间复杂度 \(\mathcal O\left(m\log^2n\right)\)

吉司机线段树

类似于最假女选手,考虑本题如何维护。

吉司机线段树区间最值操作本质上就是将数集划分为最值、非最值来分别维护,从而将区间最值操作转化为对于最值的区间加减操作。(当然,你不需要维护区间和的时候,直接对最值取最值是一样的。)

先不考虑区间加减。

以维护区间 \(\operatorname{chkmin}\) 为例,区间 \(\operatorname{chkmax}\) 是类似的。

传统线段树似乎无法很好的维护,每次操作中我们的操作对象都是那些大于 \(x\) 的数,而不是整个区间。那么我们考虑单独维护区间 \([l,r]\) 内的最大值 \(\textit{max}\)、最大值的个数 \(\textit{cnt}\)、次大值 \(\textit{max}_2\)

这些信息都是很好上传维护的。

而区间 \([l,r]\)\(x\)\(\min\) 时:

  1. \(\textit{max}\leq x\),则取 \(\min\) 后肯定不变,无需操作。

  2. \(\textit{max}_2<x<\textit{max}\),则取 \(\min\) 后所有的 \(\textit{max}\) 都变为了 \(x\),且其余所有部分都不变。

    之后令 \(\textit{max}\leftarrow x\),并且打一个懒标记 \(\textit{tagMax}\leftarrow x\) 即可。

  3. 否则,就暴力递归处理。

原论文中证明不带区间加减的区间最值操作复杂度为 \(\mathcal O(m\log n)\),但是在 \(2025\) 年末被证伪,复杂度下界均为 \(\mathcal O\left(m\log^2n\right)\)

而同时维护 \(\operatorname{chkmin},\operatorname{chkmax}\) 时,需要注意数集重合。即如果存在 \(\textit{max}=\textit{min},\textit{max}=\textit{min}_2,\textit{max}_2=\textit{min}\) 的时候要一起更新。这是因为区间里的数很少。


现在考虑区间加减。

钦定加减运算优先级高于区间最值(因为加法关于最值操作有分配律)。

这样,区间加 \(x\) 的时候,将对应的 \(\textit{max},\textit{min}\)\(x\),对应的次最值、标记也增加 \(x\) 即可。

AC 代码

其实是从最假女选手复制过来改的。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
#define int ll
typedef long long ll;
constexpr const int N=5e5;
constexpr const ll inf=0x3f3f3f3f3f3f3f3f;
int n,a[N+1];
struct segTree{
	struct node{
		int l,r;
		ll tagAdd;
		int cntMax,cntMin; 
		ll max,max2,tagMax;
		ll min,min2,tagMin;

		int size(){
			return r-l+1;
		}
	}t[N<<2|1];
	
	void up(int p){
		if(t[p<<1].max>t[p<<1|1].max){
			t[p].max=t[p<<1].max;
			t[p].cntMax=t[p<<1].cntMax;
			t[p].max2=::max(t[p<<1].max2,t[p<<1|1].max);
		}else if(t[p<<1].max==t[p<<1|1].max){
			t[p].max=t[p<<1].max;
			t[p].cntMax=t[p<<1].cntMax+t[p<<1|1].cntMax;
			t[p].max2=::max(t[p<<1].max2,t[p<<1|1].max2);
		}else{
			t[p].max=t[p<<1|1].max;
			t[p].cntMax=t[p<<1|1].cntMax;
			t[p].max2=::max(t[p<<1].max,t[p<<1|1].max2);
		}
		
		if(t[p<<1].min<t[p<<1|1].min){
			t[p].min=t[p<<1].min;
			t[p].cntMin=t[p<<1].cntMin;
			t[p].min2=::min(t[p<<1].min2,t[p<<1|1].min);
		}else if(t[p<<1].min==t[p<<1|1].min){
			t[p].min=t[p<<1].min;
			t[p].cntMin=t[p<<1].cntMin+t[p<<1|1].cntMin;
			t[p].min2=::min(t[p<<1].min2,t[p<<1|1].min2);
		}else{
			t[p].min=t[p<<1|1].min;
			t[p].cntMin=t[p<<1|1].cntMin;
			t[p].min2=::min(t[p<<1].min,t[p<<1|1].min2);
		}
	}
	void build(int p,int l,int r){
		t[p]={l,r};
		t[p].tagMax=-inf;
		t[p].tagMin=inf;
		if(l==r){
			t[p].max=t[p].min=a[l];
			t[p].max2=-inf,t[p].min2=inf;
			t[p].cntMax=t[p].cntMin=1;
			return;
		}
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		up(p);
	}
	void downAdd(int p,ll x){
		t[p].tagAdd+=x;
		t[p].max+=x;
		if(t[p].max2!=-inf){
			t[p].max2+=x;
		}
		if(t[p].tagMax!=-inf){
			t[p].tagMax+=x;
		}
		t[p].min+=x;
		if(t[p].min2!=inf){
			t[p].min2+=x;
		}
		if(t[p].tagMin!=inf){
			t[p].tagMin+=x;
		}
	}
	void downMax(int p,ll x){
		if(t[p].min>x){
			return;
		}
		t[p].tagMax=x;
		if(t[p].max2==t[p].min){
			t[p].max2=x;
		}
		if(t[p].max==t[p].min){
			t[p].max=x;
		}
		t[p].min=x;
		if(t[p].tagMin<x){
			t[p].tagMin=x;
		}
	}
	void downMin(int p,ll x){
		if(t[p].max<=x){
			return;
		}
		t[p].tagMin=x;
		if(t[p].min2==t[p].max){
			t[p].min2=x;
		}
		if(t[p].min==t[p].max){
			t[p].min=x;
		}
		t[p].max=x;
		if(t[p].tagMax>x){
			t[p].tagMax=x;
		}
	}
	void down(int p){
		if(t[p].tagAdd){
			downAdd(p<<1,t[p].tagAdd);
			downAdd(p<<1|1,t[p].tagAdd);
			t[p].tagAdd=0;
		}
		if(t[p].tagMax!=-inf){
			downMax(p<<1,t[p].tagMax);
			downMax(p<<1|1,t[p].tagMax);
			t[p].tagMax=-inf;
		}
		if(t[p].tagMin!=inf){
			downMin(p<<1,t[p].tagMin);
			downMin(p<<1|1,t[p].tagMin);
			t[p].tagMin=inf;
		}
	}
	void add(int p,int l,int r,int x){
		if(l<=t[p].l&&t[p].r<=r){
			downAdd(p,x); 
			return; 
		}
		down(p);
		if(l<=t[p<<1].r){
			add(p<<1,l,r,x);
		}
		if(t[p<<1|1].l<=r){
			add(p<<1|1,l,r,x);
		}
		up(p);
	}
	void chkmax(int p,int l,int r,int x){
		if(l<=t[p].l&&t[p].r<=r){
			if(x<=t[p].min){
				return;
			}else if(x<t[p].min2){
				downMax(p,x);
				return;
			}
		}
		down(p);
		if(l<=t[p<<1].r){
			chkmax(p<<1,l,r,x);
		}
		if(t[p<<1|1].l<=r){
			chkmax(p<<1|1,l,r,x);
		}
		up(p);
	}
	void chkmin(int p,int l,int r,int x){
		if(l<=t[p].l&&t[p].r<=r){
			if(t[p].max<=x){
				return;
			}else if(t[p].max2<x){
				downMin(p,x);
				return;
			}
		}
		down(p);
		if(l<=t[p<<1].r){
			chkmin(p<<1,l,r,x);
		}
		if(t[p<<1|1].l<=r){
			chkmin(p<<1|1,l,r,x);
		}
		up(p);
	}
	ll max(int p,int l,int r){
		if(l<=t[p].l&&t[p].r<=r){
			return t[p].max;
		}
		down(p);
		ll ans=-inf;
		if(l<=t[p<<1].r){
			ans=max(p<<1,l,r);
		}
		if(t[p<<1|1].l<=r){
			ans=::max(ans,max(p<<1|1,l,r));
		}
		return ans;
	}
}t;
main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	t.build(1,1,n);
	while(m--){
		int op,l,r,x;
		cin>>op>>l>>r;
		ll pl; 
		switch(op){
			case 1:
				cin>>x;
				t.add(1,l,r,x);
				break;
			case 2:
				cin>>x;
				t.chkmin(1,l,r,x);
				break;
			case 3:
				cin>>x;
				t.chkmax(1,l,r,x);
				break;
			case 4:
				cout<<t.max(1,l,r)<<'\n';
				break;
		}
	}
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-02-09 10:06  TH911  阅读(3)  评论(0)    收藏  举报