[ZJOI2007] 仓库建设

link

题意

\(n\) 个工厂,对于第 \(i\) 个工厂,给出:

  • \(x_i\):到第 \(1\) 个工厂的距离
  • \(p_i\):第 \(i\) 个工厂的物品数
  • \(c_i\):在第 \(i\) 个工厂建仓库的花费

每个工厂的物品只能向它后面的工厂运,运费为运送距离 * 运送物品数量。
必须将每个物品放在仓库里,求最小花费。

\(n\le 1e6\)

思路

蒟蒻最开始设的是 \(f_{i,j}\) ,表示前 \(i\) 个工厂建 \(j\) 个仓库,且第 \(j\) 个仓库建在 \(i\) 的最小花费。

然后显然,

\[f_{i,j}=min_{1\le k\le i-1}\{f_{k,j-1}+\sum_{l=k+1}^{i-1}(x_i-x_{l})\cdot p_l \}+c_i \]

然后就懵了。

时间复杂度:\(O(n^4)!!!\) ,空间复杂度:\(O(n^2)!!!\)

双双爆炸!

经过网上 \(Dalao\) 们题解的启发,我开始了优化。

看到这个式子:

\[\sum_{l=k+1}^{i-1}(x_i-x_{l})\cdot p_l \]

尝试化简

\[\sum_{l=k+1}^{i-1}(x_i-x_{l})\cdot p_l\\ =\sum_{l=k+1}^{i-1}(x_i\cdot p_l-x_{l}\cdot p_l)\\ =\sum_{l=k+1}^{i-1}x_i\cdot p_l-\sum_{l=k+1}^{i-1}x_{l}\cdot p_l\\ =x_i\cdot \sum_{l=k+1}^{i-1}p_l-\sum_{l=k+1}^{i-1}x_{l}\cdot p_l \]

然后发现 \(\sum_{l=k+1}^{i-1}p_l\)\(\sum_{l=k+1}^{i-1}x_{l}\cdot p_l\)\(i\) 并没有任何关系,直接算前缀和。

\(sp_i=\sum_{l=1}^i p_l,sxp_i=\sum_{l=1}^i x_l\cdot p_l\)

于是,

\[f_{i,j}=min_{1\le k\le i-1}\{f_{k,j-1}+x_i(sp_{i-1}-sp_{k})-(sxp_{i-1}-sxp_k)\}+c_i \]

时间复杂度:\(O(n^3)!!\) ,空间复杂度:\(O(n^2)!!!\)

还差远了。

但是观察转移方程,每次从 \(j-1\) 转移到 \(j\) ......

啊哈!滚动数组!

直接滚掉一维,空间复杂度:\(O(n)\) ,可以过了。

但时间复杂度还是 \(O(n^3)\) ,炸掉。

但我们又细品状态转移方程,好像状态的转移和 \(j\) 并没有关系......

啊哈!不用枚举 \(j\) 了!

时间复杂度:\(O(n^2)\) !

还差一点。

把方程展开:

\[f_{i,j}=min_{1\le k\le i-1}\{f_{i,j}=f_{k,j-1}+x_isp_{i-1}-x_isp_{k}-sxp_{i-1}+sxp_k\}+c_i \]

看到乘积项 \(x_isp_{k}\) ,想到斜率优化。

尝试一下。

\[f_{i,j}=f_{k,j-1}+x_isp_{i-1}-x_isp_{k}-sxp_{i-1}+sxp_k \]

状态变量为 \(i\) ,决策变量为 \(k\)

移项。

\[f_{k,j-1}+sxp_k=x_isp_k+f_{i,j}+sxp_{i-1}-x_isp_{i-1} \]

此时:

\[k=x_i\\ y=f_{k,j-1}+sxp_k\\ b=f_{i,j}+sxp_{i-1}-x_isp_{i-1}\\ x=sp_k \]

发现 \(k\) 单调增,\(x\) 也单调增。

可行!

时间复杂度:\(O(n)\)

终于拿下了!

Code

#include <bits/stdc++.h>
#define re register
#define int long long
#define ld long double
#define END system("pause")
#define inf 0x7fffffff
using namespace std;
inline int read()
{
	re int x=0,f=1;
	re char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-48;c=getchar();}
	return x*f;
}
const int N=1e6+6;
int n;
int x[N],sp[N],sxp[N],c[N];
int f[N];
int q[N],h,t;
inline int Y(int k){return f[k]+sxp[k];}
//inline int X(int k){return sp[k];}
signed main()
{
	n=read();
	for(re int i=1,p;i<=n;++i)
	{
		x[i]=read(),p=read(),c[i]=read();
		sp[i]=sp[i-1]+p;
		sxp[i]=sxp[i-1]+x[i]*p;
	}
	q[h=t=1]=0;
	for(re int i=1;i<=n;++i)
	{
		while(h<t&&Y(q[h+1])-Y(q[h])<=x[i]*(sp[q[h+1]]-sp[q[h]])) ++h;
		f[i]=f[q[h]]+x[i]*sp[i-1]-x[i]*sp[q[h]]-sxp[i-1]+sxp[q[h]]+c[i];
		//q[t-1],q[t],i
		while(h<t&& (Y(q[t])-Y(q[t-1])) * (sp[i]-sp[q[t]]) >= (Y(i)-Y(q[t])) * (sp[q[t]]-sp[q[t-1]]) ) --t;
		q[++t]=i;
	}
	printf("%lld",f[n]);
	END;
	return 0;
}
posted @ 2021-07-09 11:29  After-glow  阅读(61)  评论(0)    收藏  举报