笔记 - 吉司机线段树
前几天换的小蓝花(左下角)是不是很可爱啊
吉司机线段树
简介
吉如一老师(吉司机)发明的线段树。复杂度基于势能分析,解决区间取min/max,和记录历史min/max问题。
由于水平有限,目前只有区间取min/max。
如何搞
拿取 min 举例,取 max 同理。
俩同时支持的时候记得处理一下两者之间的关系
考虑现在要对区间 ,每个数和 取 。
设区间最大值为
如果 ,直接跑路(显然)
否则...继续递归?
这个做法显然是假的。当 很小的时候,复杂度就会被卡满。
吉司机的做法是:
维护区间 严格 次大值
如果 ,那么区间中只有最大值会变。看成减法即可。
如果要求和,就顺便维护一下最大值的个数,算贡献,就可以维护和了
否则我们再继续递归下去。
时间复杂度的证明
设线段树 上,“一类点”为: 的最大值等于父亲的最大值;二类点为 的最大值小于父亲的最大值的。
设一颗 的势能函数 为二类点的数量。显然
一次区间取 的操作更新下来,会有若干个二类点变成一类点 (因为被取 了),然后变一个二类点的花费是 (一条路径顺下来)
换句话说我们可以用 的时间把 给减一。于是我们的总复杂度不会超过
加上更多东西
带上区间加?证明还可靠么?
一次区间加,最多会涉及到 个节点。就算他们全部吧 加了 , 的最大值也就是
然后沿用上一个证明,得到算法的复杂度是
而实际问题中这个 一般会很小,并且也不是每个节点都能让 ,所以它实际上跑的和 一样快(雾)
带上历史最值?还能做吗?
能,我不会
update 2021.05.04: 现在我会了,见 这里
例题
bzoj 最假女选手
说的就是hjz吧
题面很清楚。维护的东西有点小多。
上面提到的,区间取 min/max 的时候,注意把区间整个取没了
比如我们要和 ,取 ,但是 ,然后整个区间就都是 了
顺便,就算没有这么离谱,在取 的时候对 的影响也要考虑到
写代码的时候小心一点就行了,挺长的
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 500005
#define oo 0x3f3f3f3f
#define ll long long
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define all(a) a.begin(),a.end()
#define iter(a,p) (a.begin()+p)
#define PUT(a,n) F(i,1,n) printf("%d ",a[i]); puts("");
int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
template <typename T> void Rd(T& arg){arg=I();}
template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
int n,q,a[N];
void Input()
{
n=I();
F(i,1,n) a[i]=I();
q=I();
}
struct node
// 最, 次, 最的数量
// 最大值和最小值都用这个
{
int fir,sec,cnt;
node() {fir=sec=cnt=0;}
};
node merge_max(node a,node b)
{
node ans; ans.sec=-oo;
ans.fir=max(a.fir,b.fir);
if (ans.fir==a.fir) ans.cnt+=a.cnt;
if (ans.fir==b.fir) ans.cnt+=b.cnt;
ans.sec=max(a.sec,b.sec);
if (a.fir!=b.fir) ans.sec=max(ans.sec,min(a.fir,b.fir));
return ans;
}
node merge_min(node a,node b)
{
node ans; ans.sec=oo;
ans.fir=min(a.fir,b.fir);
if (ans.fir==a.fir) ans.cnt+=a.cnt;
if (ans.fir==b.fir) ans.cnt+=b.cnt;
ans.sec=min(a.sec,b.sec);
if (a.fir!=b.fir) ans.sec=min(ans.sec,max(a.fir,b.fir));
return ans;
}
// 最大/最小的信息合并
class DriverSegmentTree
{
public:
node mx[N<<2],mn[N<<2]; ll s[N<<2],ad[N<<2];
#define ls ix<<1
#define rs ix<<1|1
#define lson ls,L,mid
#define rson rs,mid+1,R
#define inx int ix=1,int L=1,int R=n
#define curr ix,L,R
inline void up(int ix)
{
s[ix]=s[ls]+s[rs];
mx[ix]=merge_max(mx[ls],mx[rs]);
mn[ix]=merge_min(mn[ls],mn[rs]);
}
inline void addone(ll x,inx) // 单点加
{
ad[ix]+=x;
s[ix]+=x*(R-L+1);
mx[ix].fir+=x; mx[ix].sec+=x;
mn[ix].fir+=x; mn[ix].sec+=x;
}
inline void cminone(int x,inx) // 在保证 max2<x<max 的情况下, 单点取 min
{
s[ix]-=(ll)mx[ix].cnt*(mx[ix].fir-x);
mx[ix].fir=x; mn[ix].fir=min(mn[ix].fir,x);
if (mx[ix].fir==mn[ix].fir) // 考虑区间取没的情况
{
s[ix]=(ll)mx[ix].fir*(R-L+1);
mx[ix].cnt=mn[ix].cnt=(R-L+1);
mx[ix].sec=-oo; mn[ix].sec=oo;
}
else
{
mn[ix].sec=min(mn[ix].sec,x); // 和对另一边的影响
}
}
inline void cmaxone(int x,inx) // 这边是对称的
{
s[ix]+=(ll)mn[ix].cnt*(x-mn[ix].fir);
mn[ix].fir=x; mx[ix].fir=max(mx[ix].fir,x);
if (mn[ix].fir==mx[ix].fir)
{
s[ix]=(ll)mn[ix].fir*(R-L+1);
mx[ix].cnt=mn[ix].cnt=(R-L+1);
mx[ix].sec=-oo; mn[ix].sec=oo;
}
else
{
mx[ix].sec=max(mx[ix].sec,x);
}
}
inline void down(inx)
{
int mid=(L+R)>>1;
if (ad[ix])
{
addone(ad[ix],lson);
addone(ad[ix],rson);
ad[ix]=0;
}
if (mx[ls].fir>mx[ix].fir and mx[ls].sec<mx[ix].fir) cminone(mx[ix].fir,lson);
if (mx[rs].fir>mx[ix].fir and mx[rs].sec<mx[ix].fir) cminone(mx[ix].fir,rson);
if (mn[ls].fir<mn[ix].fir and mn[ls].sec>mn[ix].fir) cmaxone(mn[ix].fir,lson);
if (mn[rs].fir<mn[ix].fir and mn[rs].sec>mn[ix].fir) cmaxone(mn[ix].fir,rson);
// 简单讨论一下
}
void build(inx)
{
ad[ix]=0;
if (L==R)
{
mx[ix].fir=mn[ix].fir=s[ix]=a[L];
mx[ix].cnt=mn[ix].cnt=1;
mx[ix].sec=-oo; mn[ix].sec=oo;
return;
}
int mid=(L+R)>>1;
build(lson);
build(rson);
up(ix);
}
void add(int l,int r,int x,inx)
{
if (l<=L and R<=r)
{
addone(x,curr);
return;
}
int mid=(L+R)>>1;
down(curr);
if (l<=mid) add(l,r,x,lson);
if (mid<r) add(l,r,x,rson);
up(ix);
}
void cmin(int l,int r,int x,inx)
{
if (x>=mx[ix].fir) return;
if (l<=L and R<=r and x>mx[ix].sec)
{
cminone(x,curr);
return;
}
int mid=(L+R)>>1;
down(curr);
if (l<=mid) cmin(l,r,x,lson);
if (mid<r) cmin(l,r,x,rson);
up(ix);
}
void cmax(int l,int r,int x,inx)
{
if (x<=mn[ix].fir) return;
if (l<=L and R<=r and x<mn[ix].sec)
{
cmaxone(x,curr);
return;
}
int mid=(L+R)>>1;
down(curr);
if (l<=mid) cmax(l,r,x,lson);
if (mid<r) cmax(l,r,x,rson);
up(ix);
}
ll qsum(int l,int r,inx)
{
if (l<=L and R<=r) return s[ix];
int mid=(L+R)>>1;
down(curr);
ll ans=0;
if (l<=mid) ans+=qsum(l,r,lson);
if (mid<r) ans+=qsum(l,r,rson);
return ans;
}
int qmax(int l,int r,inx)
{
if (l<=L and R<=r) return mx[ix].fir;
int mid=(L+R)>>1;
down(curr);
int ans=-oo;
if (l<=mid) ans=max(ans,qmax(l,r,lson));
if (mid<r) ans=max(ans,qmax(l,r,rson));
return ans;
}
int qmin(int l,int r,inx)
{
if (l<=L and R<=r) return mn[ix].fir;
int mid=(L+R)>>1;
down(curr);
int ans=oo;
if (l<=mid) ans=min(ans,qmin(l,r,lson));
if (mid<r) ans=min(ans,qmin(l,r,rson));
return ans;
}
// 最基本的递归线段树, 没啥好注释的
}T;
void Sakuya()
{
T.build();
F(i,1,q)
{
int o=I();
if (o==1)
{
int l,r,x; Rd(l,r,x);
T.add(l,r,x);
}
if (o==2)
{
int l,r,x; Rd(l,r,x);
T.cmax(l,r,x);
}
if (o==3)
{
int l,r,x; Rd(l,r,x);
T.cmin(l,r,x);
}
if (o==4)
{
int l,r; Rd(l,r);
printf("%lld\n",T.qsum(l,r));
}
if (o==5)
{
int l,r; Rd(l,r);
printf("%d\n",T.qmax(l,r));
}
if (o==6)
{
int l,r; Rd(l,r);
printf("%d\n",T.qmin(l,r));
}
}
}
void IsMyWife()
{
Input();
Sakuya();
}
}
#undef int //long long
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】