BZOJ 2726 SDOI2012 任務安排
阿新 • • 發佈:2018-12-09
Problem
Solution
不難設出一個不怎麼樣的狀態f[i][j]表示前i個分j段的最小代價
考慮未來費用優化,多分一段相當於後面所有段都加上S×費用
時間複雜度
發現不知道怎麼單調佇列,所以試試考慮斜率優化,若i從j轉移比從k轉移要優,則有:
維護一個下凸包。又由於st[i]+s不一定單調,所以不能用單調佇列,而是在維護好的凸包上二分。
注意特判sf[j]和sf[k]相等的時候,f小的更優秀。不過好像寫的程式碼不同也有所不同,不然兩種都試一下吧。
時間複雜度
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;
}