省选专练之斜率优化[ZJOI2007]仓库建设

乍一看不好维护前缀和

观察前缀关系:令  X_{i}=\sum_{j=1}^{i}X_{j}

有:贡献为 (X_{i}-X_{j})*P_{i}

暨拆开:X_{i}P_{j}-X_{j}P_{j}

故设: G_{i}=\sum-X_{i}P_{i}

对于朴素DP、

转移有:

F_{i}=\sum_{j}^{i-1}min(F_{j}+G_{i-1}-G_{j}+C_{j}+(P_{i}-P_{j})X_{i})

设k为选中点

有:

F_{k}+P_{i}X_{i}-P_{k}X_{i}+G_{i-1}-G_{k}+C_{k}<F_{j}+P_{i}X_{i}-P_{j}X_{i}+G_{i-1}-G_{j}+C_{j}

消去相同项

F_{k}-P_{k}X_{i}-G_{k}+C_{k}<F_{j}-P_{j}X_{i}-G_{j}+C_{j}

不妨设 \varphi _{k}=F_{k}-G_{k}+C_{k}

\frac{\varphi _{k}-\varphi _{j}}{P_{k}-P_{j}}<X_{i}

完毕

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int N=1e6+1000;
typedef int INT;
#define int long long
int G[N];
int F[N];
int P[N];
int X[N];
int C[N];
int Q[N*3];
int head,tail;
int n;
double Y(int Id){
	return F[Id]*1.0-G[Id]*1.0;
}
double Xsum(int Id){
	return P[Id]*1.0;
}
double Slope(int k,int t){
	return ((double)Y(t)-Y(k))/(double)(Xsum(t)-Xsum(k));
}
INT main(){
//	freopen("1096.in","r",stdin);
//	freopen("1096.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&X[i],&P[i],&C[i]);
		G[i]=G[i-1]-X[i]*P[i];
	}
	for(int i=1;i<=n;i++){
		P[i]+=P[i-1];
	}
	head=1;
	tail=0;
	Q[1]=0;
	for(int i=1;i<=n;i++){
		F[i]=C[i]+P[i-1]*X[i]+G[i-1]; 
		while(head<tail&&X[i]-Slope(Q[head],Q[head+1])>eps)head++;
		int Id=Q[head];
		F[i]=min(F[i],(F[Id]+(P[i-1]-P[Id])*X[i]+G[i-1]-G[Id]+C[i]));
		while(head<tail&&Slope(Q[tail-1],Q[tail])-Slope(Q[tail],i)>eps)tail--;
		tail++;
		Q[tail]=i;
//		cout<<F[i]<<'\n';
	}
	cout<<(int)F[n]<<'\n';
	return 0;
}

 

转载于:https://www.cnblogs.com/Leo-JAM/p/10079165.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值