【BZOJ】3963: [WF2011]MachineWorks
【题意】给定n台在时间di可以买入的机器,pi买入,可在任意时间ri卖出,买入和卖出之间的持有时间每天产生gi金钱,任意时间至多持有一台机器。给定初始钱数c和总天数T,求最大收益。n<=10^5。
【算法】动态规划+斜率优化(CDQ分治)
【题解】机器按di排序,添加一台时间为T+1的机器,令f[i]表示di时不持有机器(最后一台机器在di之前卖出)的最大收益。
f[i]=max{ f[i-1] , f[j]-p[j]+r[j]+g[j]*(d[i]-d[j]-1) } , j<i
为了简化方程,令A[i]=f[j]-p[j]+r[j]-g[j]*(d[j]+1),则
f[i]=max{ f[i-1] , A[j]+g[j]*d[i] } , j<i
对于g[j]<g[k]的两个决策j和k,当k优于j时满足:
A[j]+g[j]*d[i]<A[k]+g[k]*d[i] 即 (A[j]-A[k])/(g[j]-g[k])>-d[i]
用CDQ分治维护上凸包,先按-d[i]排序,然后左子区间构造凸包(按g[]排序),右子区间顺序决策(按-d[i]排序),最后按x[]归并排序。
具体过程见CDQ分治维护斜率优化。
复杂度O(n log n)。
#include<cstdio> #include<cctype> #include<algorithm> #define ll long long using namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } const int maxn=100010; const double eps=1e-10,inf=1000000000000; int s[maxn],n,m; ll f[maxn]; struct cyc{int d,p,r,g,id,x;ll y;}a[maxn],b[maxn]; bool cmp(cyc a,cyc b){return -a.d<-b.d;} double slope(int A,int B){ if(a[A].x==a[B].x){if(a[B].y<a[A].y)return inf;else return -inf;} return 1.0*(a[A].y-a[B].y)/(a[A].x-a[B].x); } void CDQ(int l,int r){ if(l==r){ f[l]=max(f[l-1],f[l]); a[l].x=a[l].g; a[l].y=f[l]-a[l].p+a[l].r-1ll*a[l].g*(a[l].d+1); if(f[l]<a[l].p)a[l].g=-1; return; } int mid=(l+r)>>1; int x1=l-1,x2=mid; for(int i=l;i<=r;i++)if(a[i].id<=mid)b[++x1]=a[i];else b[++x2]=a[i]; for(int i=l;i<=r;i++)a[i]=b[i]; CDQ(l,mid); int top=0; for(int i=l;i<=mid;i++)if(~a[i].g){ while(top>1&&slope(s[top],i)<slope(s[top-1],s[top]))top--; s[++top]=i; } int x=1; for(int i=mid+1;i<=r;i++){ while(x<top&&slope(s[x],s[x+1])<-a[i].d)x++; if(x<=top)f[a[i].id]=max(f[a[i].id],a[s[x]].y+1ll*a[s[x]].g*a[i].d); } CDQ(mid+1,r); x1=l,x2=mid+1; for(int i=l;i<=r;i++){ if(x1==mid+1)b[i]=a[x2++];else if(x2==r+1)b[i]=a[x1++];else if(a[x1].g>a[x2].g)b[i]=a[x1++];else b[i]=a[x2++]; } for(int i=l;i<=r;i++)a[i]=b[i]; } int main(){ n=read();f[0]=read();m=read(); int T=0; while(n||f[0]||m){ T++; for(int i=1;i<=n;i++)a[i].d=read(),a[i].p=read(),a[i].r=read(),a[i].g=read(); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++)a[i].id=n-i+1; a[++n]=(cyc){m+1,0,0,0,n,0,0}; sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++)f[i]=0; CDQ(1,n); printf("Case %d: %lld\n",T,f[n]); n=read();f[0]=read();m=read(); } return 0; }
代码中使用的方程时(A[j]-A[k])/(g[j]-g[k])<-d[i] ,g[j]>g[k]。