【洛谷P5785】[SDOI2012]任務安排
阿新 • • 發佈:2021-10-29
題解
首先設計出 \(n^2\) 的 DP,\(dp(i,j)\) 表示前 \(i\) 個分成 \(j\) 段。
後來發現空間不夠,於是第二維可以壓掉,反正每一次都是從前一維狀態轉移來。
由於 \(T\) 的範圍,新加入的斜率不一定單調遞增,所以單調佇列每次不能彈出隊首元素,變成了單調棧。
仍然是維護斜率單調遞增的下凸包,但是轉移的時候二分找到最優的轉移點。
程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long const int INF = 0x3f3f3f3f,N = 3e5+10; inline ll read() { ll ret=0;char ch=' ',c=getchar(); while(!(c>='0'&&c<='9')) ch=c,c=getchar(); while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar(); return ch=='-'?-ret:ret; } ll dp[N],tmp[N]; ll s,n; ll sumT[N],sumC[N]; int q[N],l,r; inline double X(int i){return sumC[i];} inline double Y(int i){return dp[i]-sumC[i]*s;} inline int find(double t) { int L=l,R=r; if(l==r) return q[l];//這樣特判比較簡潔 while(L<R) { int mid=(L+R+1)>>1; if((Y(q[mid])-Y(q[mid-1]))<=(X(q[mid])-X(q[mid-1]))*t) L=mid; else R=mid-1; } return q[L];//注意二分的是q陣列下標 } int main() { n=read(),s=read(); for(int i=1;i<=n;i++) { sumT[i]=read(),sumT[i]+=sumT[i-1]; sumC[i]=read(),sumC[i]+=sumC[i-1]; } memset(dp,0x3f,sizeof(dp));//和暴力一樣的初始化 dp[0]=0; l=1,r=1; for(int i=1;i<=n;i++) { int j=find(sumT[i]);//二分找到轉移點 dp[i]=Y(j)+sumT[i]*(sumC[i]-sumC[j])+sumC[n]*s; while(l<r&&(X(i)-X(q[r-1]))*(Y(q[r])-Y(q[r-1]))>=(X(q[r])-X(q[r-1]))*(Y(i)-Y(q[r-1]))) r--; q[++r]=i; } printf("%lld\n",dp[n]); return 0; }