斜率优化(李超树)
不会单调队列,又看到古神拿李超树切斜率优化特别顺手,遂来学了一下,确实挺好用。
李超树
一种用来快速维护有关线段的信息的数据结构,一般是 的,在斜率优化题中用到的操作是 的(没有斜率优化题会卡 吧)。
原理是递归来判断线段在一段区间内的优劣,这个在做题时不用理会。只需要记住板子,然后把斜截式方程代到里面,按具体题目微调一下,然后就可以了。OIer只需要求出 dp 方程,转化成斜截式,代到李超树里就行了,可是李超树要考虑的事情就比较多了。
拿一道P5785 任务安排的代码当例子,按这个模板写就行。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls t[now].l
#define rs t[now].r
const ll N=3*114514,M=1919810,inf=1145141919810;
ll n,s,ti[N],f[N],dp[N],id[N];
ll rt,cnt;
struct xx{
ll val,id;
}a[N];
bool cmp(xx x,xx y){
return x.val<y.val;
}
struct tree{
ll k,b;
ll l,r;
}t[4*N];
ll get(ll id,ll x){
return t[id].k*x+t[id].b;
}
void pushdown(ll &now,ll l,ll r,ll k,ll b){
if(!now) now=++cnt; //这里不一定
ll mid=(l+r)>>1;
ll la=get(now,a[l].val),ra=get(now,a[r].val);
ll lx=k*a[l].val+b,rx=k*a[r].val+b;
ll mid1=get(now,a[mid].val),mid2=k*a[mid].val+b;
if(la<=lx&&ra<=rx) return;
if(la>lx&&ra>rx){
t[now].k=k,t[now].b=b;
return;
}
if(mid1>mid2){
swap(la,lx),swap(ra,rx);
swap(t[now].k,k),swap(t[now].b,b);
}
if(la>lx) pushdown(ls,l,mid,k,b);
if(ra>rx) pushdown(rs,mid+1,r,k,b);
}
ll query(ll now,ll l,ll r,ll tar){
if(!now) return inf;
ll mid=(l+r)>>1,ans=get(now,a[tar].val);
if(tar<=mid) ans=min(ans,query(ls,l,mid,tar));
else ans=min(ans,query(rs,mid+1,r,tar));
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>s;
for(int i=1;i<=n;++i){
cin>>ti[i]>>f[i];
ti[i]+=ti[i-1],f[i]+=f[i-1];
a[i].val=ti[i]+s,a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i) id[a[i].id]=i;
for(int i=1;i<=n;++i){
dp[i]=query(rt,1,n,id[i])+f[n]*s+ti[i]*f[i];
pushdown(rt,1,n,-f[i],dp[i]);
}
cout<<dp[n];
return 0;
}
斜率优化
题一般都是能很简单求出 的转移式,但是复杂度要求在 以内的,这个时候可以考虑斜率优化。因为使用的是李超树,所以斜截式就要列成 的形式。一般来说,只与 相关的项放到 ,至于 相关的项和常数项放在 ,同时和 相关的放在 。然后有些题会有些特殊操作,需要微调。
鸽
頑張って