P5785 [SDOI2012]任务安排 斜率优化

题意:

戳这里

分析:

  • 暴力

(f[i][j]) 表示枚举到第 (i) 个点,分成 (j) 段时的最小代价,

(f[i][j]=min(f[k][j-1]+(s*j+sumt[i]-sumt[k])*(sumc[i]-sumc[k])))

然后用李超树维护转移,复杂度 (O(n^2log n))

  • 正解

在 巨佬 的提示下发现可以优化 DP 状态,我们发现 DP 状态的第二维可以通过更改枚举状态优化掉,因为他在转移式中只出现了一次,而且与 (k) 无关

我们设 (f[i]) 表示枚举到第 (i) 个点,之后的每一个点已经加上了由于之前重启操作带来的代价,转移式就变为了

(f[i]=min(f[j]+s*(sumc[n]-sumc[j])+sumt[i]*(sumc[i]-sumc[j])))

这样把之后每一个点由于之前操作带来的影响都消掉了

然后我们发现这个式子长的很斜率优化

我们令 (k< j)(j)(k) 转移更优,那么存在

(f[j]+s*(sumc[n]-sumc[j])+sumt[i]*(sumc[i]-sumc[j])<f[k]+s*(sumc[n]-sumc[k])+sumt[i]*(sumc[i]-sumc[k]))

化简得到

(f[j]-s*sumc[j]-sumt[i]*sumc[j]<f[k]-s*sumc[k]-sumt[i]*sumc[k])

(largefrac{(f[j]-s*sumc[j])-(f[k]-sumc[k])}{sumc[j]-sumc[k]}<sumt[i])

但是关键在于 (|t_i|<2^8) ,也就是说 (sumt[i]) 并不单调,所以我们不能单调队列维护,我们只能维护一个下凸壳,然后每次二分寻找最优决策点转移

复杂度 (O(nlog n))

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline long long read()
	{
		long long x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 3e5+5;
	long long n,s,l,r;
	long long f[maxn],c[maxn],ti[maxn],prec[maxn],pret[maxn],q[maxn];
	
	long long upn(int j,int k)
	{
		return (f[j]-s*prec[j])-(f[k]-s*prec[k]);
	}
	
	long long dwn(int j,int k)
	{
		return (prec[j]-prec[k]);
	}
	
	int solve(long long x)
	{
		int res=-1,ll=l,rr=r-1,mid;
		while(ll<=rr)
		{
			mid=(ll+rr)>>1;
			if(upn(q[mid+1],q[mid])>x*dwn(q[mid+1],q[mid])) res=mid,rr=mid-1;
			else ll=mid+1;
		}
		return res==-1?q[r]:q[res];
	}
	
	void work()
	{
		memset(f,0x7f,sizeof(f));
		n=read();s=read();
		for(int i=1;i<=n;i++) ti[i]=read(),c[i]=read(),prec[i]=prec[i-1]+c[i],pret[i]=pret[i-1]+ti[i];
		l=0;r=0;f[0]=0;
		for(int i=1;i<=n;i++)
		{
			int pos=solve(pret[i]);
			f[i]=f[pos]+s*(prec[n]-prec[pos])+(prec[i]-prec[pos])*pret[i];
			while(l<r&&dwn(i,q[r])*upn(q[r],q[r-1])>=dwn(q[r],q[r-1])*upn(i,q[r])) r--;
			q[++r]=i;
		}
		printf("%lld
",f[n]);
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/14134242.html