jzoj3169-[GDOI2013模拟4]生产汽车【斜率优化dp,单调队列,二分】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/89281824

正题


题目大意

n n 个人 m m 辆车。
人有 t i t_i ,车有 f j f_j 。第i个人修第j俩车时间是 t i f j t_i*f_j
一辆车要每个人都修一遍,且一个人修好后要求下一个人没有工作。对于每辆车找一个修理开始时间要求总修理时间最小(得按顺序修)。


解题思路

定义 t i = i = 1 m t i t_i=\sum_{i=1}^mt_i
g i g_i 表示第 i i 辆车开始的时间,然后答案就是 g m + f n s i g_m+f_n*s_i
且有 g i = g i 1 + max { t k f i 1 t k 1 f i } g_i=g_{i-1}+\max\{t_k*f_{i-1}-t_{k-1}*f_i\}
时间复杂度 O ( n m ) O(nm)
愉快 T L E TLE ,我们考虑斜率优化
对于决策 j , k j,k ,且 k k j j
那有 t k f i 1 t k 1 f i > t j f i 1 t j 1 f i t_k*f_{i-1}-t_{k-1}*f_i>t_j*f_{i-1}-t_{j-1}*f_i
( t k t j ) f i 1 > ( t k 1 t j 1 ) f i \Rightarrow (t_k-t_j)*f_{i-1}>(t_{k-1}-t_{j-1})*f_i
t k t j t k 1 t j 1 > f i f i 1 \Rightarrow \frac{t_k-t_j}{t_{k-1}-t_{j-1}}>\frac{f_i}{f_{i-1}}

然后我们发现 f i f i 1 \frac{f_i}{f_{i-1}} 并不是单调递增的,但是 t k t j t k 1 t j 1 \frac{t_k-t_j}{t_{k-1}-t_{j-1}} 肯定越大越优,所以我们可以按照 t k t j t k 1 t j 1 \frac{t_k-t_j}{t_{k-1}-t_{j-1}} 维护一个单调递增的单调队列,然后就可以对于每个 f i f i 1 \frac{f_i}{f_{i-1}} 在单调队列上二分。
时间复杂度 O ( m log n ) O(m\log n)


c o d e code

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=110000; 
ll n,m,t[N],f[N],q[N],g[N],tail;
double tan_(ll x,ll y)
{return (t[x]-t[y])/(double)(t[x-1]-t[y-1]);}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=n;i++)
	  scanf("%lld",&t[i]),t[i]+=t[i-1];
	for(ll i=1;i<=m;i++)
	  scanf("%lld",&f[i]);
	for(ll i=1;i<=n;i++){
		while(tail>1&&tan_(i,q[tail])>tan_(q[tail],q[tail-1])) 
		  tail--;
		q[++tail]=i;
	}
	for(ll i=2;i<=m;i++){
		ll l=0,r=tail;
		double k=(double)f[i]/(double)f[i-1];
		while(l<r){
			ll mid=(l+r)/2;
			if(tan_(q[mid+1],q[mid])>k) l=mid+1;
			else r=mid;
		}
		g[i]=g[i-1]+t[q[l]]*f[i-1]-t[q[l]-1]*f[i];
	}
	printf("%lld",g[m]+t[n]*f[m]);
} 

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/89281824