●BZOJ 3963 [WF2011]MachineWorks
题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=3963
题解:
斜率优化DP,CDQ分治。
先按时间排序。(规定以下内容的第i台机器的卖出时间D[i]大于第i-1台机器的卖出时间D[i-1])
定义DP[i]表示在在第i台机器可以交易的那天,卖出所有机器后能够得到的最大收益,(最后答案是DP[all_day+1])
转移显然:
$DP[i]=DP[i-1]$
$DP[i]=max(DP[j]+(D_i-D_j-1)*G_j-P_j+R_j) (j<i且DP[j]>=P[j])$
令$Y_j=DP[j]-(D_j+1)*G_j-P_j+R_j$,如果存在两个转移点k,j且G[k]<G[j],假设j点优于k点
那么 $Y_j-Y_k>-D_i(G_j-G_k)$
$\quad\quad\frac{Y_j-Y_k}{G_j-G_k}>-D_i$
那么得到结论,如果 G[k]<G[j],且Slope(j,k)>-D[i]的话,则j点优于k点。
同时如果存在三个转移来源点:k,j,i,满足G[k]<G[j]<G[i],
同时Slope(i,j)>Slope(j,k),则j点无效。
所以对于每个来源点二元组(G[j],Y[j]),只需要在平面上维护一个上凸壳即可。
但是G不单调,所以用CDQ分治。
对于分治的每一层l~r,先递归处理左边l~mid,然后把左边按G从小到大排序,并维护好上凸壳。
由于D单调递增,所以遍历一边凸壳以及右边mid+1~r进行贡献就好。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100050 #define ll long long using namespace std; ll Y[MAXN],G[MAXN],D[MAXN],P[MAXN],R[MAXN],DP[MAXN]; int h[MAXN],N,C,T,S; bool cmp( int i, int j){ return D[i]<D[j]; } struct Moque{ int q[MAXN],l,r; #define Slope(i,j) (1.0*(Y[i]-Y[j])/(G[i]-G[j])) void Reset(){l=1; r=0;} void Push( int i){ if (l<=r&&G[i]==G[q[r]]) { if (Y[i]>Y[q[r]]) r--; else return ;} while (l+1<=r&&Slope(i,q[r])>Slope(q[r],q[r-1])) r--; q[++r]=i; } int Query( int i){ while (l+1<=r&&Slope(q[l+1],q[l])>-D[i]) l++; return q[l]; } }Q; void solve( int l, int r){ static int tmp[MAXN],cl,cr,p; static ll MAXDP; if (l==r) return ( void )(Y[h[l]]=DP[h[l]]-(D[h[l]]+1)*G[h[l]]-P[h[l]]+R[h[l]]); int mid=(l+r)>>1; solve(l,mid); //之后,左边G单调 Q.Reset(); MAXDP=0; for ( int i=l;i<=mid;i++){ MAXDP=max(MAXDP,DP[h[i]]); if (DP[h[i]]>=P[h[i]]) Q.Push(h[i]); //上凸壳斜率单减 } //Di单增,-Di单减,正序枚举就好。 for ( int i=mid+1,j;i<=r;i++){ j=Q.Query(h[i]); DP[h[i]]=max(DP[h[i]],MAXDP); DP[h[i]]=max(DP[h[i]],DP[j]+(D[h[i]]-D[j]-1)*G[j]-P[j]+R[j]); } solve(mid+1,r); cl=l; cr=mid+1; p=l; while (cl<=mid||cr<=r){ if (cl>mid) tmp[p]=h[cr],cr++; else if (cr>r||G[h[cl]]<G[h[cr]]) tmp[p]=h[cl],cl++; else tmp[p]=h[cr],cr++; p++; } for ( int i=l;i<=r;i++) h[i]=tmp[i]; } int main(){ int Cas=0; while (1){ memset (DP,0, sizeof (DP)); scanf ( "%d%d%d" ,&N,&S,&T); if (N==0&&DP[1]==0&&T==0) break ; memset (Y,0, sizeof (Y)); for ( int i=1,a,b,c,d;i<=N;i++){ scanf ( "%d%d%d%d" ,&a,&b,&c,&d); h[i]=i; D[i]=a; P[i]=b; R[i]=c; G[i]=d; } N++; D[N]=T+1; h[N]=N; sort(h+1,h+N+1,cmp); DP[h[1]]=S; solve(1,N); printf ( "Case %d: %lld\n" ,++Cas,DP[N]); } return 0; } |
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步