发现以前学习的斜率优化全是假的。
学习到了新的斜率优化,比原先的那个更方便一些。
形如
的dp,其中
是要计算的dp,
都是已知量,
是算出
后可以得知的量。
那么直接将
视为一个关于
的变量,那么可以直接维护动态半平面交一样的东西;一种方法是和维护动态凸包类似,维护个平衡树,每次二分即可;或者可以直接李超线段树;或者可以套个分治然后静态的半 半平面交(所有半平面方向一致所以比朴素半平面交好写);如果是区间询问的话可以类似SDOI向量集套一个线段树维护半平面交;如果询问点单调或者加入直线斜率单调都可以套单调数据结构。
这种方法可以一眼看出函数的斜率和截距,以及要维护的是上还是下凸包。
回到正文:
题目大意:有一个方程形如
,其中
是非负整数,
是单调不增的数列。
那么首先考虑那个看起来像是斜率的max,可以用一个CDQ分治去处理。
假设分治区间[L,R],先求完左半边[L,mid],然后考虑[L,mid]对(mid,R]的影响。
考虑
,
可以看做两个部分:找到一个
,使得
,其中
。那么这部分转移到
,那个转移中的max都会是
,那么在这个区间
中找一个最小的
即可;否则这个转移中的max就会和
没有关系,可以预先用线段树维护好半平面交。注意到直线的斜率是不降的,而询问的值又是不升的,所以两部分都可以线性,总复杂度
。
(貌似有
神仙做法但是并不会XD)
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=100010<<2;
lint val[N],f[N],suf[N],mnf[N];int t[N],w[N],l[N];
struct Line{
lint k,b;Line(lint _k=0,lint _b=0) { k=_k,b=_b; }
inline db is(const Line &L)const { return ((db)L.b-b)/((db)k-L.k); }
inline lint F(lint x)const { return k*x+b; }
inline ostream& show()const { return debug(k)sp,debug(b); }
};
namespace Convex_space{
lint k[N],b[N];
struct Convex{
vector<Line> L;int c;
inline int build(lint *_k,lint *_b,int n)
{
L.resize(n);int t=0,ns=n;n=0;
for(int i=0,j=0;i<ns;k[n++]=_k[i],i=++j)
for(b[n]=_b[i];j+1<ns&&_k[j+1]==_k[i];j++) b[n]=min(b[n],_b[j+1]);
for(int i=0;i<n;L[t++]=Line(k[i],b[i]),i++)
while(t>1&&L[t-2].is(L[t-1])>=L[t-1].is(Line(k[i],b[i]))) t--;
return L.resize(t),c=t-1,0;
}
inline lint query(lint x) { while(c&&L[c-1].is(L[c])>=x) c--;return L[c].F(x); }
}convex[N];
}using Convex_space::convex;
inline int build(int x,int l,int r)
{
convex[x].build(val+l,f+l,r-l+1);
int mid=(l+r)>>1;if(l==r) return 0;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
return 0;
}
inline lint query(int x,int l,int r,int s,int t,lint p)
{
if(s>t) return LLONG_MAX;
if(s<=l&&r<=t) return convex[x].query(p);
int mid=(l+r)>>1;lint ans=LLONG_MAX;
if(s<=mid) ans=min(ans,query(x<<1,l,mid,s,t,p));
if(mid<t) ans=min(ans,query(x<<1|1,mid+1,r,s,t,p));
return ans;
}
inline int solve(int L,int R)
{
if(L==R) return 0;int mid=(L+R)>>1;
solve(L,mid),val[mid]=t[mid+1],mnf[mid]=f[mid];
for(int i=mid-1;i>=L;i--)
val[i]=max(val[i+1],(lint)t[i+1]),mnf[i]=min(mnf[i+1],f[i]);
build(1,L,mid);
for(int i=mid+1,mx=0,ps=mid+1;i<=R;i++)
{
mx=max(mx,t[i]);while(ps>L&&val[ps-1]<=mx) ps--;if(l[i]>mid) continue;
f[i]=min(f[i],mnf[max(ps,l[i])]+mx*suf[i+1]);
f[i]=min(f[i],query(1,L,mid,max(l[i],L),ps-1,suf[i+1]));
}
return solve(mid+1,R);
}
int main()
{
int n=inn();rep(i,1,n) l[i]=inn(),t[i]=inn(),w[i]=inn();
for(int i=n;i;i--) suf[i]=suf[i+1]+w[i];rep(i,1,n) f[i]=LLONG_MAX;
return solve(0,n),!printf("%lld\n",f[n]);
}