题意:
一道斜率优化板子题。
我们首先设
其中
展开转化移项,可以得到:
我们把
同理,经过类似的推理(详情见玩具装箱解析),用单调队列维护一下即可。
步骤:
- 取出最优的点,即每次在队头判断是否
,因为最优点是下凸包中第一个斜率大于 的点,所以如果小于 就弹出队头; - 按照转移方程更新;
- 维护斜率单调递增,判断
这个点是否可以被加入下凸包,即要使得 ,所以弹出所有满足 的队尾; - 将
入队。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
#define INF 1e9
using namespace std;
const int maxn=5010;
const double Pi=acos(-1.0);
template<class T>void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(ch<'0'||ch>'9') {f|=(ch=='-');ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}
int n,s,head,tail,q[maxn];
double sum[maxn],fsum[maxn],dp[maxn];
double x(int i)
{
return fsum[i];
}
double y(int i)
{
return dp[i]-s*fsum[i];
}
double slope(int i,int j)
{
return (y(i)-y(j))/(x(i)-x(j));
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
sum[i]=sum[i-1]+a;
fsum[i]=fsum[i-1]+b;
}
head=tail=1;
for(int i=1;i<=n;i++)
{
while(head<tail&&slope(q[head],q[head+1])<sum[i])
head++;
dp[i]=dp[q[head]]+sum[i]*(fsum[i]-fsum[q[head]])+s*(fsum[n]-fsum[q[head]]);
while(head<tail&&slope(i,q[tail])<slope(q[tail],q[tail-1]))
tail--;
q[++tail]=i;
}
printf("%d",(int)dp[n]);
return 0;
}