KTT 小记
来源#
来自 EI 的 2020 年的论文《浅谈函数最值的动态维护》。
适用范围#
给出一些形如
思想与实现#
使用线段树,记录一个阈值
为了方便计算,我们线段树上的点存的最大值改为用直线存而不是一个数字。一条直线
考虑如何求
定义 “转移” 为将两条直线按上述 “合并” 的过程。不难发现,
对于修改,打标记的时候将
根据 EI 的论文,可以证明,时间复杂度为
点击查看代码
#define pil pair<line,ll>
struct line{
ll k,b;
line(){k=b=0;}
line(ll K,ll B){k=K, b=B;}
void add(ll v){b+=k*v;}
};
pil merg(line x,line y){
if(x.k<y.k||(x.k==y.k&&x.b<y.b)) swap(x,y);
if(x.b>=y.b) return mkp(x,inf);
return mkp(y,(y.b-x.b)/(x.k-y.k));
}
const pil operator+ (const pil &x,const pil &y){
pil tmp=merg(x.fi,y.fi);
return mkp(tmp.fi,min(min(x.se,y.se),tmp.se));
}
struct KTT{
ll tag[maxn<<2]; pil a[maxn<<2];
void addtag(ll p,ll v){
tag[p]+=v, a[p].se-=v;
a[p].fi.add(v);
}
void pushdown(ll p){
if(!tag[p]) return;
addtag(p<<1,tag[p]);
addtag(p<<1|1,tag[p]);
tag[p]=0;
}
void upd(ll p){
if(a[p].se>=0) return;
pushdown(p);
upd(p<<1), upd(p<<1|1);
a[p]=a[p<<1]+a[p<<1|1];
}
void build(ll p,ll l,ll r){
if(l==r){
a[p]=mkp(line(0,-inf),inf);
return;
} ll mid=l+r>>1;
build(p<<1,l,mid), build(p<<1|1,mid+1,r);
a[p]=a[p<<1]+a[p<<1|1];
}
void modify(ll p,ll l,ll r,ll ql,ll qr,ll v){
if(ql<=l&&r<=qr){
addtag(p,v);
upd(p); return;
}
ll mid=l+r>>1; pushdown(p);
if(ql<=mid) modify(p<<1,l,mid,ql,qr,v);
if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,v);
a[p]=a[p<<1]+a[p<<1|1];
}
void rep(ll p,ll l,ll r,ll x,line w){
if(l==r){
a[p].fi=w, a[p].se=inf;
return;
} ll mid=l+r>>1;
pushdown(p);
if(x<=mid) rep(p<<1,l,mid,x,w);
else rep(p<<1|1,mid+1,r,x,w);
a[p]=a[p<<1]+a[p<<1|1];
}
}T;
入门题#
设
枚举上一个点
每次加入区间会把
具体操作看代码
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pir pair<ll,ll>
#define pil pair<line,ll>
#define fi first
#define se second
#define mkp make_pair
#define max(a,b) ((a)>(b)? (a):(b))
#define min(a,b) ((a)<(b)? (a):(b))
#define pb push_back
#define ls(p) a[p].lc
#define rs(p) a[p].rc
#define ad(a,b) ((a)+(b)>=mod? (a)+(b)-mod:(a)+(b))
using namespace std;
const ll maxn=1e6+10, inf=1e17;
struct line{
ll k,b;
line(){k=b=0;}
line(ll K,ll B){k=K, b=B;}
void add(ll v){b+=k*v;}
};
pil merg(line x,line y){
if(x.k<y.k||(x.k==y.k&&x.b<y.b)) swap(x,y);
if(x.b>=y.b) return mkp(x,inf);
return mkp(y,(y.b-x.b)/(x.k-y.k));
}
const pil operator+ (const pil &x,const pil &y){
pil tmp=merg(x.fi,y.fi);
return mkp(tmp.fi,min(min(x.se,y.se),tmp.se));
}
struct KTT{
ll tag[maxn<<2]; pil a[maxn<<2];
void addtag(ll p,ll v){
tag[p]+=v, a[p].se-=v;
a[p].fi.add(v);
}
void pushdown(ll p){
if(!tag[p]) return;
addtag(p<<1,tag[p]);
addtag(p<<1|1,tag[p]);
tag[p]=0;
}
void upd(ll p){
if(a[p].se>=0) return;
pushdown(p);
upd(p<<1), upd(p<<1|1);
a[p]=a[p<<1]+a[p<<1|1];
}
void build(ll p,ll l,ll r){
if(l==r){
a[p]=mkp(line(0,-inf),inf);
return;
} ll mid=l+r>>1;
build(p<<1,l,mid), build(p<<1|1,mid+1,r);
a[p]=a[p<<1]+a[p<<1|1];
}
void modify(ll p,ll l,ll r,ll ql,ll qr,ll v){
if(ql<=l&&r<=qr){
addtag(p,v);
upd(p); return;
}
ll mid=l+r>>1; pushdown(p);
if(ql<=mid) modify(p<<1,l,mid,ql,qr,v);
if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,v);
a[p]=a[p<<1]+a[p<<1|1];
}
void rep(ll p,ll l,ll r,ll x,line w){
if(l==r){
a[p].fi=w, a[p].se=inf;
return;
} ll mid=l+r>>1;
pushdown(p);
if(x<=mid) rep(p<<1,l,mid,x,w);
else rep(p<<1|1,mid+1,r,x,w);
a[p]=a[p<<1]+a[p<<1|1];
}
}T;
ll t,n,m,l,r,f[maxn],a[maxn];
vector<ll>vec[maxn];
int main()
{
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&m,&n);
T.build(1,1,n);
for(ll i=1;i<=m;i++){
scanf("%lld%lld",&l,&r);
vec[r].pb(l);
}
for(ll i=1;i<=n+1;i++){
f[i]=max(0ll,T.a[1].fi.b);
if(i>n) break;
scanf("%lld",a+i);
T.rep(1,1,n,i,line(a[i],f[i]));
for(ll j:vec[i])
T.modify(1,1,n,j,i,1);
vec[i].clear();
}
printf("%lld\n",f[n+1]);
}
return 0;
}
维护转移#
这个应该算是比较正规的板子。
考虑平常我们是如何维护最大子段和的?维护四个数
其实和之前说的维护区间最大值是一样的,只不过变成了维护四个数。两个东西取
然后直线的斜率就是所维护的段的长度,显然区间加
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pir pair<ll,ll>
#define pil pair<line,ll>
#define fi first
#define se second
#define mkp make_pair
#define max(a,b) ((a)>(b)? (a):(b))
#define min(a,b) ((a)<(b)? (a):(b))
#define pb push_back
#define ls(p) a[p].lc
#define rs(p) a[p].rc
#define ad(a,b) ((a)+(b)>=mod? (a)+(b)-mod:(a)+(b))
using namespace std;
const ll maxn=4e5+10, inf=1e17;
ll n,m,op,l,r,k;
struct line{
ll k,b;
line(){k=b=0;}
line(ll K,ll B){k=K,b=B;}
const line operator+ (const line t) const{
return line(k+t.k,b+t.b);
}
void add(ll v){b+=k*v;}
};
pil merg(line x,line y){
if(x.k<y.k||(x.k==y.k&&x.b<y.b)) swap(x,y);
if(x.b>=y.b) return mkp(x,inf);
return mkp(y,(y.b-x.b)/(x.k-y.k));
}
struct node{
line sum,lmx,rmx,mx; ll x;
node(){x=inf;}
node(line a,line b,line c,line d,ll e){sum=a, lmx=b, rmx=c, mx=d, x=e;}
const node operator+ (const node t) const{
node res; pil tmp;
res.x=min(x,t.x);
res.sum=sum+t.sum;
tmp=merg(lmx,sum+t.lmx), res.lmx=tmp.fi, res.x=min(res.x,tmp.se);
tmp=merg(t.rmx,rmx+t.sum), res.rmx=tmp.fi, res.x=min(res.x,tmp.se);
tmp=merg(mx,t.mx), res.mx=tmp.fi, res.x=min(res.x,tmp.se);
tmp=merg(res.mx,rmx+t.lmx), res.mx=tmp.fi, res.x=min(res.x,tmp.se);
return res;
}
}a[maxn<<2];
struct KTT{
ll tag[maxn<<2];
void addtag(ll p,ll v){
tag[p]+=v; a[p].x-=v;
a[p].sum.add(v), a[p].lmx.add(v), a[p].rmx.add(v), a[p].mx.add(v);
}
void pushdown(ll p){
if(!tag[p]) return;
addtag(p<<1,tag[p]);
addtag(p<<1|1,tag[p]);
tag[p]=0;
}
void upd(ll p){
if(a[p].x>=0) return;
pushdown(p);
upd(p<<1), upd(p<<1|1);
a[p]=a[p<<1]+a[p<<1|1];
}
void build(ll p,ll l,ll r){
if(l==r){
ll x;
scanf("%lld",&x);
line d(1,x);
a[p]=node(d,d,d,d,inf);
return;
}
ll mid=l+r>>1;
build(p<<1,l,mid), build(p<<1|1,mid+1,r);
a[p]=a[p<<1]+a[p<<1|1];
}
void modify(ll p,ll l,ll r,ll ql,ll qr,ll v){
if(ql<=l&&r<=qr){
addtag(p,v);
upd(p); return;
} pushdown(p);
ll mid=l+r>>1;
if(ql<=mid) modify(p<<1,l,mid,ql,qr,v);
if(mid<qr) modify(p<<1|1,mid+1,r,ql,qr,v);
a[p]=a[p<<1]+a[p<<1|1];
}
node query(ll p,ll l,ll r,ll ql,ll qr){
if(ql<=l&&r<=qr){
upd(p);
return a[p];
}
pushdown(p);
ll mid=l+r>>1;
if(qr<=mid) return query(p<<1,l,mid,ql,qr);
if(mid<ql) return query(p<<1|1,mid+1,r,ql,qr);
return query(p<<1,l,mid,ql,qr)+query(p<<1|1,mid+1,r,ql,qr);
}
}T;
int main()
{
scanf("%lld%lld",&n,&m);
T.build(1,1,n);
while(m--){
scanf("%lld%lld%lld",&op,&l,&r);
if(op==1){
scanf("%lld",&k);
T.modify(1,1,n,l,r,k);
} else{
node ret=T.query(1,1,n,l,r);
printf("%lld\n",max(0ll,ret.mx.b));
}
}
return 0;
}
出处:https://www.cnblogs.com/Sktn0089/p/18027988
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下