XVII.CF311B Cats Transport
推式子时间到~~~
我们首先对题目中的\(d_i\)做前缀和,求出每座山距离原点距离;
然后对于第\(i\)只猫,如果一个饲养员在\(t_i-d_{h_i}\)时刻以后出发就可以接到它;
注意,饲养员可以在负时刻就出发!!!我之前想多了以为只能在非负时刻出发而纳闷了好半天
我们设\(t_i-d_{h_i}\)为新的\(t_i\),然后将所有的\(t_i\)排序。
然后开始DP:
设\(f[i][j]\)表示:前\(i\)只猫,派出\(j\)个人,的最优时间。再设\(s_i\)表示\(t_i\)的前缀和。
则有\(f[i][j]=\min\limits_{k=0}^{i-1}\{f[k][j-1]+t_i*(i-k)-(s_i-s_k)\}\)
我们这样就可以写出\(O(m^2p)\)的代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,p,d[100100],t[100100],s[100100],f[100100][110],qwq=0x3f3f3f3f3f3f3f3f;
signed main(){
scanf("%lld%lld%lld",&n,&m,&p),memset(f,0x3f3f3f3f,sizeof(f));
for(int i=2;i<=n;i++)scanf("%lld",&d[i]),d[i]+=d[i-1];
// for(int i=1;i<=n;i++)printf("%lld ",d[i]);puts("");
for(int i=1,x,y;i<=m;i++)scanf("%lld%lld",&x,&y),t[i]=y-d[x];
// printf("%lld\n",res);
// for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
sort(t+1,t+m+1);
for(int i=1;i<=m;i++)s[i]=s[i-1]+t[i];
// for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
f[0][0]=0;
for(int j=1;j<=p;j++)for(int i=1;i<=m;i++)for(int k=0;k<i;k++)f[i][j]=min(f[i][j],f[k][j-1]+(i-k)*t[i]-(s[i]-s[k]));
for(int i=1;i<=p;i++)qwq=min(qwq,f[m][i]);
printf("%lld\n",qwq);
return 0;
}
然后考虑斜率优化一下:
在\(f[i][j]\)中,我们按照列优先(先枚举\(j\))的顺序进行DP;并且,设\(f[i][j-1]\)为\(F[i]\)。
设\(j<k<i\)(不是同一个\(j\))。则如果\(j\)比\(k\)优,则有:
\(F_j+t_i*(i-j)-(s_i-s_j)<F_k+t_i*(i-k)-(s_i-s_k)\)
拆开
\(F_j+i*t_i-j*t_i-s_i+s_j<F_k+i*t_i-k*t_i-s_i-s_k\)
抵消
\(F_j-j*t_i+s_j<F_k-k*t_i+s_k\)
移项
\(F_j-F_k+s_j-s_k<(j-k)*t_i\)
除过去(注意\(j-k\)是负数)
\(\dfrac{F_j-F_k+s_j-s_k}{j-k}>t_i\)
右边的\(t_i\)是递增的(排过序了),因此可以采用单调队列维护斜率;然后维护一个下凸壳即可。复杂度\(O(mp)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,p,d[100100],t[100100],s[100100],f[100100][110],qwq=0x3f3f3f3f3f3f3f3f,l,r,q[100100];
signed main(){
scanf("%lld%lld%lld",&n,&m,&p),memset(f,0x3f3f3f3f,sizeof(f));
for(int i=2;i<=n;i++)scanf("%lld",&d[i]),d[i]+=d[i-1];
// for(int i=1;i<=n;i++)printf("%lld ",d[i]);puts("");
for(int i=1,x,y;i<=m;i++)scanf("%lld%lld",&x,&y),t[i]=y-d[x];
// printf("%lld\n",res);
// for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
sort(t+1,t+m+1);
for(int i=1;i<=m;i++)s[i]=s[i-1]+t[i];
// for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
f[0][0]=0;
for(int j=1;j<=p;j++){
l=r=0;
for(int i=1;i<=m;i++){
while(r-l&&(f[q[l]][j-1]-f[q[l+1]][j-1]+s[q[l]]-s[q[l+1]])>=(q[l]-q[l+1])*t[i])l++;
f[i][j]=f[q[l]][j-1]+(i-q[l])*t[i]-(s[i]-s[q[l]]);
while(r-l&&(f[q[r-1]][j-1]-f[q[r]][j-1]+s[q[r-1]]-s[q[r]])*(q[r]-i)>=(f[q[r]][j-1]-f[i][j-1]+s[q[r]]-s[i])*(q[r-1]-q[r]))r--;
q[++r]=i;
}
}
for(int i=1;i<=p;i++)qwq=min(qwq,f[m][i]);
printf("%lld\n",qwq);
return 0;
}