洛谷 P2120 [ZJOI2007]仓库建设

题目链接

Step1

朴素DP

for(int i=1;i<=n;i++){
	    for(int j=0;j<i;j++){
		    long long x=0;
			for(int k=j+1;k<=i;k++) x+=p[k]*(a[i]-a[k]);
			dp[i]=min(dp[i],dp[j]+x+c[i]);
		}
	}

Step 2

前缀和优化

for(int i=1;i<=n;i++) s1[i]=s1[i-1]+p[i],s2[i]=s2[i-1]+p[i]*a[i];

for(int i=1;i<=n;i++){
	for(int j=0;j<i;j++){
		dp[i]=min(dp[i],dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]); 
    }
}

Step 3

单调队列优化

/*

dp[i]=min(dp[i],dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]);

j<k&& k is better

dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]>dp[k]+a[i]*(s1[i]-s1[k])-(s2[i]-s2[k])+c[i]

dp[j]+a[i]*s1[i]-a[i]*s1[j]-s2[i]+s2[j]+c[i]>dp[k]+a[i]*s1[i]-a[i]*s1[k]-s2[i]+s2[k]+c[i]

dp[j]-a[i]*s1[j]+s2[j]>dp[k]-a[i]*s1[k]+s2[k]

(dp[j]+s2[j])-(dp[k]+s2[k])>a[i]*(s1[j]-s1[k])

(dp[j]+s2[j])-(dp[k]+s2[k])/(s1[j]-s1[k])>a[i]

(dp[k]+s2[k])-(dp[j]+s2[j])/(s1[k]-s1[j])>a[i]

a[i]递增

斜率也要递增

单调队列维护 
*/

#include<bits/stdc++.h>
using namespace std;

int read(){
	int x=0; char c=getchar(); int flag=1;
	while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
	while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
	return x*flag;
}

const int N=1e6+50;

int n;
long long a[N],p[N],c[N];
long long s1[N],s2[N];
long long dp[N];

int q[N];
int l,r;

signed main(){
    n=read();
    for(int i=1;i<=n;i++){
	    a[i]=read(),p[i]=read(),c[i]=read();
	    dp[i]=1e18;
	}

    for(int i=1;i<=n;i++) s1[i]=s1[i-1]+p[i],s2[i]=s2[i-1]+p[i]*a[i];

    l=r=1; q[1]=0;
    for(int i=1;i<=n;i++){
	    while(r>l){
		    if((dp[q[l+1]]+s2[q[l+1]]-dp[q[l]]-s2[q[l]])<a[i]*(s1[q[l+1]]-s1[q[l]])) ++l;//当前的队头斜率小于a[i],即队头无法对答案有贡献,弹出
		    else break;
		}
		
		dp[i]=min(dp[i],dp[q[l]]+a[i]*(s1[i]-s1[q[l]])-(s2[i]-s2[q[l]])+c[i]);
		
		while(r>l){
		    if((dp[q[r]]+s2[q[r]]-dp[q[r-1]]-s2[q[r-1]])*(s1[i]-s1[q[r]])>=(dp[i]+s2[i]-dp[q[r]]-s2[q[r]])*(s1[q[r]]-s1[q[r-1]])) --r;//维护队尾斜率递增
			else break; 
		}
		
		q[++r]=i;
	}

	printf("%lld\n",dp[n]);
	
    return 0;
}
posted @ 2020-01-27 11:44  zhuzihan  阅读(92)  评论(0编辑  收藏  举报