1. 程式人生 > 實用技巧 >CF311B Cats Transport 題解

CF311B Cats Transport 題解

Link

CF311B Cats Transport

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;
}