正题
题目大意
有nnn个人mmm辆车。
人有tit_iti,车有fjf_jfj。第i个人修第j俩车时间是ti∗fjt_i*f_jti∗fj。
一辆车要每个人都修一遍,且一个人修好后要求下一个人没有工作。对于每辆车找一个修理开始时间要求总修理时间最小(得按顺序修)。
解题思路
定义ti=∑i=1mtit_i=\sum_{i=1}^mt_iti=∑i=1mti
设gig_igi表示第iii辆车开始的时间,然后答案就是gm+fn∗sig_m+f_n*s_igm+fn∗si
且有gi=gi−1+max{tk∗fi−1−tk−1∗fi}g_i=g_{i-1}+\max\{t_k*f_{i-1}-t_{k-1}*f_i\}gi=gi−1+max{tk∗fi−1−tk−1∗fi}
时间复杂度O(nm)O(nm)O(nm)
愉快TLETLETLE,我们考虑斜率优化
对于决策j,kj,kj,k,且kkk比jjj优
那有tk∗fi−1−tk−1∗fi>tj∗fi−1−tj−1∗fit_k*f_{i-1}-t_{k-1}*f_i>t_j*f_{i-1}-t_{j-1}*f_itk∗fi−1−tk−1∗fi>tj∗fi−1−tj−1∗fi
⇒(tk−tj)∗fi−1>(tk−1−tj−1)∗fi\Rightarrow (t_k-t_j)*f_{i-1}>(t_{k-1}-t_{j-1})*f_i⇒(tk−tj)∗fi−1>(tk−1−tj−1)∗fi
⇒tk−tjtk−1−tj−1>fifi−1\Rightarrow \frac{t_k-t_j}{t_{k-1}-t_{j-1}}>\frac{f_i}{f_{i-1}}⇒tk−1−tj−1tk−tj>fi−1fi
然后我们发现fifi−1\frac{f_i}{f_{i-1}}fi−1fi并不是单调递增的,但是tk−tjtk−1−tj−1\frac{t_k-t_j}{t_{k-1}-t_{j-1}}tk−1−tj−1tk−tj肯定越大越优,所以我们可以按照tk−tjtk−1−tj−1\frac{t_k-t_j}{t_{k-1}-t_{j-1}}tk−1−tj−1tk−tj维护一个单调递增的单调队列,然后就可以对于每个fifi−1\frac{f_i}{f_{i-1}}fi−1fi在单调队列上二分。
时间复杂度O(mlogn)O(m\log n)O(mlogn)
codecodecode
#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]);
}