luogu P4360 [CEOI2004]锯木厂选址 斜率优化dp

#include<map>
#include<queue>
#include<time.h>
#include<limits.h>
#include<cmath>
#include<ostream>
#include<iterator>
#include<set>
#include<stack>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define rep_1(i,m,n) for(int i=m;i<=n;i++)
#define mem(st) memset(st,0,sizeof st)
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef pair<double,double> pdd;
const int inf = 0x3f3f3f3f;
const int N=3e4+10;
int q[N];
int n;
int sum;
int s[N],d[N],w[N];
int dp[N];
double Y(int i)
{
	return 1.0*d[i]*s[i];
}
double X(int i)
{
	return s[i];
}
double K(int i)
{
	return d[i];
}
double calc(int i,int j)
{
	return 1.0*(Y(i)-Y(j))/(X(i)-X(j));
}
int main()
{
	int ans=2e9+1;
	cin>>n;
	for(int i=1; i<=n; i++)
		cin>>w[i]>>d[i];
	for(int i=n; i; i--)
		d[i]+=d[i+1];
	for(int i=1; i<=n; i++)
		s[i]=s[i-1]+w[i],sum+=d[i]*w[i];
	//dis[i]表示距离的后缀和
	//sum[i]表示树的重量的前缀和
	//totsum表示所有树一开始全部运送的山脚下的花费
	//dp[i]=totsum-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])
	//dis[j]*sum[j]=dis[i]*sum[j]-dis[i]*sum[i]-dp[i]+totsum
	//dis[i]递减,所以维护的是上凸包 
	int hh=0,tt=0;
	for(int i=1; i<=n; i++)
	{
		while(hh<tt && calc(q[hh],q[hh+1])>K(i))
			hh++;
		dp[i]=sum-d[q[hh]]*s[q[hh]]-d[i]*(s[i]-s[q[hh]]);
		ans=min(ans,dp[i]);
		while(hh<tt && calc(i,q[tt-1])>calc(q[tt],q[tt-1]))
			tt--;
		q[++tt]=i;
	}
	printf("%d
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12831042.html