网上的题解...状态就没有一个和我一样的...这让我有些无从下手...
分析:
我们考虑,正常的斜率优化满足x(i)单调递增,k(i)单调递增,那么我们就可以只用维护一个单调队列满足对于当前的x(i)有最小值即可,因为x(i)满足单调递增。这样的话,我们就可以维护一个单调队列让队首元首最小。而这道题,可以发现有部分数据满足x(i)单调递增,那么直接裸上就可以,但是由于时间有负数,所以x(i)并不满足单调性。但是由于k(i)仍然满足单调性,因此,我们依然可以发现更新x(i)的时候满足斜率优化的性质,也就是我们已经可以将已经完全被覆盖的直线忽略。(即:我们依然可以维护一个大凹包)
考虑n^2的DP方程:f[i]表示当前的最后一段区间在以i为终点时的最小花费。
转移:f[i]=min{f[j]+(sum1[i]-sum1[j]+m)*(sum2[n]-sum2[j])};
之后,我们发现在我们维护的队列中,对于相同的x(i),不同的j,更新的f[i]具有单峰性,那么导函数具有单调性,二分导函数,找到最大值,之后更新答案,剩下的就是斜率优化的常规操作了。
附上代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
using namespace std;
#define N 300005
#define ll long long
#define K(x) (-sum2[x])
#define B(x) (f[x]-sum1[x]*sum+sum1[x]*sum2[x]-m*sum2[x])
#define Y(x,y) (K(y)*sum1[x]+B(y))
int n,q[N];
ll f[N],sum1[N],sum2[N],sum,m;
bool cmp(int i,int j,int k)
{
ll t1=(K(j)-K(k))*(B(i)-B(k));
ll t2=(K(i)-K(k))*(B(j)-B(k));
return t1<=t2;
}
bool check(int p1,int p2,int i)
{
return /*f[p2]-sum2[p2]*(sum1[i]+m)*/Y(i,p2)</*f[p1]-(sum1[i]+m)*sum2[p1]*/Y(i,p1);
}
int main()
{
scanf("%d%lld",&n,&m);
for(int i=1,x,y;i<=n;i++)
{
scanf("%d%d",&x,&y);
sum1[i]=sum1[i-1]+x;
sum2[i]=sum2[i-1]+y;
}
sum=sum2[n];int t=0;memset(f,0x3f,sizeof(f));f[0]=0;
for(int i=1;i<=n;i++)
{
int l=0,r=t;
while(l<r)
{
int mid=(l+r)>>1;
if(check(q[mid],q[mid+1],i))l=mid+1;
else r=mid;
}
// printf("%d\n",q[l]);
f[i]=Y(i,q[l])+sum*sum1[i]+m*sum;
while(t&&cmp(q[t-1],q[t],i))t--;
q[++t]=i;
}
printf("%lld\n",f[n]);return 0;
}