1. 程式人生 > >BZOJ 2726 SDOI2012 任務安排

BZOJ 2726 SDOI2012 任務安排

Problem

BZOJ

Solution

不難設出一個不怎麼樣的狀態f[i][j]表示前i個分j段的最小代價

f[i][j]=min(f[k][j1]+(Sj+st[i])(sf[i]sf[k]))

考慮未來費用優化,多分一段相當於後面所有段都加上S×費用

f[i]=min(f[j]+st[i](sf[i]sf[j])+S(sf[n]sf[j])) f[i]=st[i]+sf[i]+Ssf[n]+min(f[j]st[i]sf[j]Ssf[j]))

時間複雜度O(n2)

發現不知道怎麼單調佇列,所以試試考慮斜率優化,若i從j轉移比從k轉移要優,則有:

f[j]f[k]+st[i](sf[k]sf[j])+S(sf[k]sf[j])<0 f[j]f[k]<(st[i]+S)(sf[j]sf[k]) f[j]f[k]sf[j]sf[k]<st[i]+S

維護一個下凸包。又由於st[i]+s不一定單調,所以不能用單調佇列,而是在維護好的凸包上二分。

注意特判sf[j]和sf[k]相等的時候,f小的更優秀。不過好像寫的程式碼不同也有所不同,不然兩種都試一下吧。

時間複雜度O(nlogn)

Code

#include <cstdio>
#define rg register
using namespace
std; typedef long long ll; const int maxn=1000010; const double INF=1e20; template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;} template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;} template <typename Tp> inline void read(Tp &x) { x=0;int f=0;char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=1,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); if(f) x=-x; } int n,s,tp,st[maxn],sf[maxn],stk[maxn]; ll f[maxn]; double slope(int j,int k) { if(sf[j]==sf[k]) return f[j]<f[k]?INF:-INF; return 1.0*(f[j]-f[k])/(sf[j]-sf[k]); } int find(int k) { int l=1,r=tp,m,res=1; while(l<=r) { m=(l+r)>>1; if(slope(stk[m-1],stk[m])<=k) res=m,l=m+1; else r=m-1; } return stk[res]; } int main() { read(n);read(s); for(rg int i=1;i<=n;i++) { read(st[i]);st[i]+=st[i-1]; read(sf[i]);sf[i]+=sf[i-1]; } stk[++tp]=0; for(rg int i=1;i<=n;i++) { int k=find(st[i]+s); f[i]=f[k]+(ll)st[i]*(sf[i]-sf[k])+(ll)s*(sf[n]-sf[k]); while(tp>1&&slope(stk[tp-1],stk[tp])>=slope(stk[tp],i)) tp--; stk[++tp]=i; } printf("%lld\n",f[n]); return 0; }