洛谷P4360[CEOI2004]锯木厂选址

孤街浪徒 提交于 2020-02-21 22:39:41

https://www.luogu.com.cn/problem/P4360

做两遍dp,dp[k][i]表示前i个位置放k个锯木厂的最小值

先把整个顺序反过来,那么0的位置就是山脚的锯木厂

设sum[i],f[i]是sum[i]w[i]前缀和,f2[i]是w[i]的前缀和

dp[1][i]=f[i]-f[k]-sum[k+1](f2[i]-f2[k])+dp[0][k]

展开成f2[k]sum[k+1]-f[k]+dp[0][k]=f2[i]sum[k+1]+dp[1][i]-f[i]

k=f2[i]递增,sum[k+1]递增,希望截距dp[1][i]-f[i]尽可能小

画图分析得用单调队列维护一个下凸包

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

const int maxl=2e4+10;
typedef long long ll;

int n;
ll w[maxl],d[maxl],sum[maxl],f[maxl],f2[maxl];
ll dp[3][maxl];
struct node
{
	ll x,y;
}s[maxl];

inline void prework()
{
	scanf("%d",&n);
	for(int i=n;i>=1;i--)
		scanf("%lld%lld",&w[i],&d[i]);
	for(int i=1;i<=n;i++)
	{
		sum[i]=sum[i-1]+d[i];
		f[i]=f[i-1]+w[i]*sum[i];
		f2[i]=f2[i-1]+w[i];
		dp[0][i]=f[i];
	}
}

inline ll calc(ll k,int id)
{
	return s[id].y-k*s[id].x;
}

inline bool cmpk(node a,node b,node c)
{
	return 1.0*(b.y-a.y)*(c.x-b.x)>=1.0*(c.y-b.y)*(b.x-a.x);
}

inline void solv(int k)
{
	int head=1,tail=0;
	node d=node{sum[1],0};
	s[++tail]=d;
	for(int i=1;i<=n;i++)
	{
		while(head<tail && calc(f2[i],head)>=calc(f2[i],head+1))
			head++;
		dp[k][i]=calc(f2[i],head)+f[i];
		d=node{sum[i+1],f2[i]*sum[i+1]-f[i]+dp[k-1][i]};
		while(head<tail && cmpk(s[tail-1],s[tail],d))
			tail--;
		s[++tail]=d; 
	}
}

inline void mainwork()
{
	for(int k=1;k<=2;k++)
		solv(k);
}

inline void print()
{
	printf("%lld",dp[2][n]);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!