CF311B Cats Transport 題解
阿新 • • 發佈:2020-12-01
Link
Solve
這道題和2018的擺渡車有點像
對於每隻貓,我們預處理出應該什麼從\(H_1\)出發才剛好接到,設\(A_i=T_i-\sum^{H_i}_{j=1} D-j\)
把\(A_i\)從大到小排序後就很像擺渡車了
設\(F[i][j]\)表示前\(i\)個鏟屎官帶走了前\(j\)個貓,的時間總和最小,\(S_i\)表示\(A_i\)的字首和
容易寫出轉移方程,第\(i\)個鏟屎官帶走從\(k+1\)到\(j\)只貓
\[F[i][j]=min{(F[i-1][k]+A_j \ast (j-k)-(S_j-S_k))} \]這屆列舉的事件複雜度是\(O(PM^2)\)
把獅子轉化成\(kx+b\)的形式
\[A_j \ast k+(F[i][j]-A_j \ast j+S_j)=S_k+F[i-1][k] \]我們這裡把\(i\)看成常量,把\(j\)看成狀態變數,把\(k\)是決策變數
發現\(A_j\)是遞增的,\(k\)也是遞增的,直接單調佇列維護就好了
#include<bits/stdc++.h> using namespace std; const int maxn=200005; typedef long long LL; LL D[maxn],S_D[maxn],A[maxn],S_A[maxn],DP[105][maxn],N,M,P,H[maxn],T[maxn],hed=1,til=0,ans; int i,Q[maxn],j; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } inline LL X(LL j){return j;} inline LL Y(LL j){return S_A[j]+DP[i-1][j];} inline long double calc(int i,int j){return (long double)(Y(j)-Y(i))/(X(j)-X(i));} int main(){ freopen("CF311B.in","r",stdin); freopen("CF311B.out","w",stdout); N=read();M=read();P=read(); for(i=2;i<=N;i++)D[i]=read(),S_D[i]=S_D[i-1]+D[i]; for(i=1;i<=M;i++)H[i]=read(),T[i]=read(); for(i=1;i<=M;i++)A[i]=T[i]-S_D[H[i]]; sort(A+1,A+1+M); for(i=1;i<=M;i++)S_A[i]=S_A[i-1]+A[i]; for(j=1;j<=M;j++)DP[1][j]=A[j]*j-S_A[j]; ans=DP[1][M]; for(i=2;i<=P;i++){ hed=1,til=0;Q[++til]=0; for(j=1;j<=M;j++){ while(hed<til&&calc(Q[hed+1],Q[hed])<=A[j])++hed; int k=Q[hed]; DP[i][j]=DP[i-1][k]+A[j]*(j-k)-(S_A[j]-S_A[k]); while(hed<til&&calc(Q[til],Q[til-1])>=calc(Q[til-1],j))--til; Q[++til]=j; } ans=min(ans,DP[i][M]); } printf("%lld\n",ans); return 0; }