【斜率优化】运输小猫

-----思路------

一个小贪心:
(a_i=t_i-sum_{j=1}^{h_i}d_j),那么饲养员就必须在a_i时刻之后从1号山出发
这只猫的等待时间就是(t-a_i)
显然按照(a_i)从小到大排序后,每个饲养员一次都应该带走连续的几只
(f[i][j])表示前i个饲养员带走前j只猫咪的最小等待时间
状态转移方程为:
(f[i][j]=min_{0<=k<j}{f[i-1][k]+a_j*(j-k)-(s_j-s_k)})

枚举(k),求出(f[i][j]),复杂度为(O(p*m^2))

将外层循环(i)看成定值,将j看成状态变量,k看成决策变量

(min)函数拆开得

(f[i-1][k]+s_k=a_j*k+f[i][j]-a_j*j+s_j)

把k作为横坐标,(f[i-1][k]+s_k)作为纵坐标,那么截距最小时,(f[i][j])最小

可以用单调队列来维护下凸壳

-----debug-------

1.二位数组开了个1e5*1e5的,ce了
2.一堆()写的乱了,很迷
3.忘记在第一层for循环的时候把队列的指针重置一下了

#include<bits/stdc++.h>
#define int long long 
using namespace std;
inline int read() {
    char c = getchar();
    int x = 0, f = 1;
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
const int N=1e5+2020;
int n,m,p;
int q[N];
int f[110][N],sumd[N],suma[N],a[N];
signed main()
{
	n=read(),m=read(),p=read();
	for(int i=2;i<=n;++i)
	{
		int high=read();
		sumd[i]=sumd[i-1]+high;
	}
	for(int i=1;i<=m;++i)
	{
		int h=read(),t=read();
		a[i]=t-sumd[h];
	}
	sort(a+1,a+1+m);
	int l=0,r=0;
	for(int i=1;i<=m;++i) suma[i]=suma[i-1]+a[i];
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=p;++i)
	{
		q[1]=0;
		l=1;r=1;
		for(int j=1;j<=m;++j)
		{
			while(l<r&&f[i-1][q[l+1]]+suma[q[l+1]]-f[i-1][q[l]]
			-suma[q[l]]<=a[j]*(q[l+1]-q[l])) l++;
			f[i][j]=f[i-1][q[l]]+suma[q[l]]-q[l]*a[j]+j*a[j]-suma[j];
			while(l<r&&(f[i-1][q[r]]+suma[q[r]]-f[i-1][q[r-1]]-
			suma[q[r-1]])*(j-q[r])>(f[i-1][j]+suma[j]-f[i-1][q[r]]-suma[q[r]])*(q[r]-q[r-1])) r--;
			q[++r]=j;
		}
	}
	cout<<f[p][m];
	return 0;
}
原文地址:https://www.cnblogs.com/pyyyyyy/p/12674148.html