有\(n\)我的按編號從\(1\)到\(n\)排成一列,每一個人有三個屬性值\(l_i\)、\(t_i\)和\(w_i\),如今要將這些人分紅連續的若干段,記第\(i\)段(編號\(l\)到編號\(r\))中最大的\(t\)值爲\(maxt\),那麼這段的代價就是\(maxt\sum\limits_{j=r+1}^nw_j\),而且有限制,若是一段中最大的編號爲\(x\),那麼編號爲\(l_x\)的人不能在這個段內,求劃分段的最小代價ios
數據範圍\(1\leq n\leq 10^5\)c++
首先能夠列一個樸素的dp方程:優化
記\(f[i]\)表示前\(i\)我的的劃分最小代價,那麼有\(f[i]=min(f[j-1]+suf[i+1]*mx(j,i))\),其中\(j\leq i\),\(suf\)表示\(w\)的後綴和,\(mx(l,r)\)表示\(l\)到\(r\)這段中\(t\)的最大值ui
而後考慮怎麼優化這個東西spa
get到一個操做:處理這種帶\(mx\)的轉移能夠藉助單調棧code
咱們用單調棧維護\(t\)的最大值,假設當前處理到第\(i\)我的,前\(i-1\)個\(f\)值都已經求出,如今咱們要求出\(f[i]\),此時根據單調棧中存的元素咱們能夠將\(i\)前面的位置劃分紅若干段,每段裏面\(t\)的最大值\(maxt\)相同ip
而根據上面的\(dp\)式子,對於\(maxt\)相同的一段,\(suf[i+1]\)是定值,那麼\(min(f[j-1]+suf[i+1]*maxt)\)顯然在\(f[j-1]\)取\(min\)的時候最小,因爲前面的\(f\)已經所有求出,也就是說對於\(maxt\)相同的一段,這個\(min\)值是肯定的get
因此其實咱們要作的是比較這若干段\(maxt\)誰更優,而後發現這個東西能夠斜率優化,推一推獲得一個\(\frac{min_f[j]-min_f[k]}{maxt_j-maxt_k}>\)(與\(suf[i+1]\)有關的常數,沒記錯應該是帶個負號不想再推一遍了qwq)這樣的形式,也就是說咱們將\((maxt,min_f)\)當作點,維護上凸殼就行了,每次查詢的時候由於另外一邊也是單調的因此不須要二分也能夠直接單調棧掃string
最後一個問題就是怎麼維護凸殼:咱們如今要支持一個刪除點、新加點、以及區間查詢(下標就是單調棧中的下標,之因此是區間查詢是由於有\(l\)的限制)的操做,這裏有一個比較好的處理方式(其實就是二進制分組)it
實現的方式的話我用的是線段樹:首先將\(n\)補成\(2\)的整數次冪,而後對於一個長度爲\(2\)的正整數冪的區間(其實就是線段樹上每一個節點了),只有在這個區間內每個位置都有點的時候才計算這個區間的凸殼,一旦一個區間變得不完整就將這個區間原來維護的凸殼刪掉,查詢的時候一直走直到碰到一個範圍內的計算出來的凸殼才返回它的答案(而不是像普通線段樹同樣一遇到範圍內的區間就返回)
而後就作完了ovo
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> #define ll long long #define pb push_back using namespace std; const int N=1e5+10,TOP=20; const ll inf=1LL<<60; struct Dot{ ll x,y; Dot(ll _x=0,ll _y=0){x=_x; y=_y;} friend ll cross(Dot a,Dot b){return a.x*b.y-a.y*b.x;} friend Dot operator - (Dot a,Dot b){return Dot(a.x-b.x,a.y-b.y);} friend bool operator < (Dot a,Dot b){return a.x==b.x?a.y<b.y:a.x<b.x;} void print(){printf("(%lld,%lld)",x,y);} }; void print(vector<Dot> x){ int sz=x.size(); for (int i=0;i<sz;++i) x[i].print(); printf("\n"); } namespace Seg{/*{{{*/ const int N=::N*4; int ch[N][2],full[30],cnt[N],p[N]; vector<Dot> h[N],tmp; Dot st[N]; int n,tot; void _build(int x,int l,int r){ if (l==r) return; int mid=l+r>>1; ch[x][0]=++tot; _build(ch[x][0],l,mid); ch[x][1]=++tot; _build(ch[x][1],mid+1,r); } void build(int _n){ n=1; while(n<_n) n<<=1; tot=1; _build(1,1,n); } void get_hull(int x){ int sz; tmp.clear(); sz=h[ch[x][0]].size(); for (int i=0;i<sz;++i) tmp.pb(h[ch[x][0]][i]); sz=h[ch[x][1]].size(); for (int i=0;i<sz;++i) tmp.pb(h[ch[x][1]][i]); sort(tmp.begin(),tmp.end()); int top=0; sz=tmp.size(); for (int i=0;i<sz;++i){ while (top>1&&cross(tmp[i]-st[top-1],st[top]-st[top-1])>=0) --top; st[++top]=tmp[i]; } h[x].clear(); p[x]=0; for (int i=1;i<=top;++i) h[x].pb(st[i]); } void _modify(int x,int d,int lx,int rx,Dot &delta,int dep){ if (lx==rx){h[x].pb(delta); return;} ++cnt[x]; int mid=lx+rx>>1; if (d<=mid) _modify(ch[x][0],d,lx,mid,delta,dep+1); else _modify(ch[x][1],d,mid+1,rx,delta,dep+1); if (cnt[x]==rx-lx+1){ if (full[dep]){ //if (full[dep]==13) printf("+33\n"); get_hull(full[dep]); } full[dep]=x; } } void modify(int d,Dot delta){_modify(1,d,1,n,delta,1);} ll get_val(int x,ll suf){ /*int l=0,r=h[x].size()-1,mid,ret=r; if (h[x].size()==1){ return h[x][0].x*suf+h[x][0].y; } while (l<=r){ mid=l+r>>1; if (h[x][mid].y-h[x][mid+1].y>=-suf*(h[x][mid].x-h[x][mid+1].x)) ret=mid,l=mid+1; else r=mid-1; } return h[x][ret].x*suf+h[x][ret].y;*/ int sz=h[x].size()-1; while (p[x]<sz&&h[x][p[x]].x*suf+h[x][p[x]].y>h[x][p[x]+1].x*suf+h[x][p[x]+1].y) ++p[x]; return h[x][p[x]].x*suf+h[x][p[x]].y; } ll _query(int x,int l,int r,int lx,int rx,ll suf){ if (l<=lx&&rx<=r&&h[x].size()) return get_val(x,suf); int mid=lx+rx>>1; if (r<=mid) return _query(ch[x][0],l,r,lx,mid,suf); else if (l>mid) return _query(ch[x][1],l,r,mid+1,rx,suf); else return min(_query(ch[x][0],l,mid,lx,mid,suf),_query(ch[x][1],mid+1,r,mid+1,rx,suf)); } ll query(int l,int r,ll suf){return l>r?inf:_query(1,l,r,1,n,suf);} void _del(int x,int d,int lx,int rx,int dep){ h[x].clear(); if (lx==rx) return; --cnt[x]; if (full[dep]==x) full[dep]=0; int mid=lx+rx>>1; if (d<=mid) _del(ch[x][0],d,lx,mid,dep+1); else _del(ch[x][1],d,mid+1,rx,dep+1); } void del(int d){_del(1,d,1,n,1);} }/*}}}*/ int l[N],t[N],w[N],st[N]; ll suf[N],f[N],mn[N][TOP+1]; int n,m; void init(){ suf[n+1]=0; for (int i=n;i>=1;--i) suf[i]=suf[i+1]+w[i]; Seg::build(n); } ll get_mn(int l,int r){//min_f int len=r-l+1,lg=(int)(log(1.0*len)/log(2.0)); return min(mn[r][lg],mn[l+(1<<lg)-1][lg]); } void solve(){ int top=0,p; f[0]=0; st[++top]=0; for (int i=1;i<=n;++i){ while (top&&t[st[top]]<=t[i]) Seg::del(top),--top; st[++top]=i; //printf("#%d\n",top); Seg::modify(top,Dot(t[i],get_mn(st[top-1],i-1))); p=upper_bound(st+1,st+1+top,l[i])-st; f[i]=1LL*t[st[p]]*suf[i+1]+get_mn(l[i],st[p]-1); f[i]=min(f[i],Seg::query(p+1,top,suf[i+1])); mn[i][0]=f[i];//(i-(1<<j)+1)~i for (int j=1;i-(1<<j-1)>=1;++j) mn[i][j]=min(mn[i][j-1],mn[i-(1<<j-1)][j-1]); } printf("%lld\n",f[n]); } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d%d%d",l+i,t+i,w+i); init(); solve(); }