题意:直接看题意即可
题解:第一眼看上去能否贪心,发现没啥好办法,然后考虑dp,按照T从头到尾考虑发现也是没啥好办法,从后往前进行dp,我们设dp[i]为当前在i下强行修建车站的花费,然后我们发现递推式:
然后考虑展开,我们再设sumt[]为R[]*T[]的前缀和,之后再设sum[]为R[]的前缀和,然后写出式子:
然后将与min无关的挪出去,写出式子:
然后设y=dp[j]-sumt[j],设k=-T[i],设x=sum[j],之后可以发现斜率k是不断递增的,然后x也是不断递增,然后点的横坐标也在不断递增,这就是斜率dp的模型了,之后用单调队列维护一个下凸壳即可。
提示一下,就是最后还要多加一个点,就是排序前需要再加一个sta[n+1].t=0,sta[n+1].r=0的点,因为我们从后往前考虑,没有这个点相当于所有点到了n这个点就停了,实际上还有一段没有算,最后只要把在0点修建的这个车站的费用减去即可,这个点坑了我一晚上,在床上想了想就出来了,感觉自己的斜率dp代码挺好的,利用叉积不用考虑斜率的浮点数比较,还是非常靠谱的。
附上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e4+50;
ll n,m;
struct node{
ll t,r;
node(ll t=0,ll r=0):t(t),r(r){}
};
node sta[maxn];
bool cmp(node a,node b)
{
return a.t>b.t;
}
struct point{
ll x,y;
point(ll x=0,ll y=0):x(x),y(y){}
};
point q[maxn];
int head,tail;
ll multi(point o,point a,point b)
{
return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
ll dp[maxn];
ll sumt[maxn],sum[maxn];
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&sta[i].t,&sta[i].r);
}
n++;
sta[n].t=0;sta[n].r=0;
sort(sta+1,sta+n+1,cmp);
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+sta[i].r;
sumt[i]=sumt[i-1]+sta[i].r*sta[i].t;
}
dp[0]=0;
head=tail=0;
q[tail++]=point(0,0);
for(int i=1;i<=n;i++){
while(head+1<tail&&q[head].y+sta[i].t*q[head].x>=q[head+1].y+sta[i].t*q[head+1].x){
head++;
}
dp[i]=q[head].y+sta[i].t*q[head].x+sumt[i]-sta[i].t*sum[i];
if(i!=n){
dp[i]+=m;
}
point p=point(sum[i],dp[i]-sumt[i]);
while(head+1<tail&&multi(q[tail-2],q[tail-1],p)<0){
tail--;
}
q[tail++]=p;
}
printf("%lld\n",dp[n]);
return 0;
}