BZOJ 3963: [WF2011]MachineWorks 斜率优化 + splay动态维护凸包
Description
你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器。原来的那一台生产机器已经坏了,所以你要去为公司买一台新的生产机器。你的任务是在转型期内尽可能得到更大的收益。在这段时间内,你要买卖机器,并且当机器被ACM公司拥有的时候,操控这些机器以获取利润。因为空间的限制,ACM公司在任何时候都只能最多拥有一台机器。
在转型期内,有若干台可能卖出的机器。作为先进机器的专家,对于每台机器Mi,你已经知道了其价格Pi和可以买入的日期Di。注意,如果不在第Di天买入机器Mi,那么别的人也会买走这一台机器,也就是说,以后你将没有机会购买这台机器了。如果ACM的钱低于一台机器的价格,那么你显然不可能买到这一台机器。
如果你在第Di天买入了机器Mi,那么ACM公司可以从第(Di)+1天开始使用这一台机器。每使用这台机器一天,就可以为公司创造出Gi美元的收益。
你可以决定要在买入之后的某一天,以一定的折扣价卖出这一台机器。收购市场对于每一台机器,都有一个折扣价Ri。你不能在卖出的那一天使用机器,但是你可以在卖出的那一天再买入一台新的。
在转型期结束后,ACM公司会卖掉当前所拥有的机器。你的任务就是最大化转型期间ACM公司可以得到的收入。
Input
输入包含若干组测试用例。每一组测试用例的第一行有3个正整数N,C和D。N是将会卖出的机器的台数(N<=10^5),C是在转型期开始时公司拥有的美元数量(C<=10^9),D是转型期持续的天数(D<=10^9)。
之后的N行每一行描述了一台机器的情况。每一行有4个正整数Di,Pi,Ri和Gi,分别表示这台机器卖出的时间,购买这台机器需要的美元数量,卖出这台机器的折扣价和使用这台机器可以得到的利润。这些数字满足1<=Di<=D,1<=Ri<Pi<=10^9且1<=Gi<=10^9.
最后一组测试用例后面的一行由3个0组成,表示输入数据。
Output
对于每一组测试用例,输出测试用例的编号,之后给出ACM公司在第D+1天结束后可以得到的最大数量的美元。
请依照下面给出的样例输出。
题解:令 $f_{i}$ 表示 $1$ ~ $i$ 的最大收入
则 $f_{i}=f_{j}-P_{j}+R_{j}+G_{j}\times(D_{i}-D{j}-1)$
化成一次函数形式:
$-D_{i}\times G_{j}+F_{i}=f_{j}-P_{j}+R_{j}-G_{j}D_{j}-G_{j}$
$x=G_{j}$,$y=f_{j}-P_{j}+R_{j}-G_{j}D_{j}-G_{j}$,$slope=-D_{j}$
可以用斜率优化来求解.
然而,$x$ 并不单调,所以要用平衡树来动态维护这个凸包
我选择了 $splay$
一定要特别注意横坐标相等的情况,一定特判是无限大还是无限小
化成一次函数形式:
$-D_{i}\times G_{j}+F_{i}=f_{j}-P_{j}+R_{j}-G_{j}D_{j}-G_{j}$
$x=G_{j}$,$y=f_{j}-P_{j}+R_{j}-G_{j}D_{j}-G_{j}$,$slope=-D_{j}$
可以用斜率优化来求解.
然而,$x$ 并不单调,所以要用平衡树来动态维护这个凸包
我选择了 $splay$
一定要特别注意横坐标相等的情况,一定特判是无限大还是无限小
#include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define maxn 300000 #define inf 10000000000000009 #define ll long long using namespace std; const long double eps = 1e-10; int root; int idxx[maxn]; struct Splaytree { #define get(x) (ch[f[x]][1]==x) int cnt; int ch[maxn][2],f[maxn]; long double X[maxn],Y[maxn],lk[maxn],rk[maxn]; // i 在前, j 在后 long double slope(int i,int j) { if(fabs(X[i]-X[j])<=eps) return Y[j]>Y[i] ? (long double)inf : (long double)-inf; return (Y[i]-Y[j])/(X[i]-X[j]); } inline void rotate(int x) { int old=f[x],fold=f[old],which=get(x); ch[old][which]=ch[x][which^1],f[ch[old][which]]=old; ch[x][which^1]=old,f[old]=x,f[x]=fold; if(fold) ch[fold][ch[fold][1]==old]=x; } inline void splay(int x,int &tar) { int fa,u=f[tar]; for(;(fa=f[x])!=u;rotate(x)) if(f[fa]!=u) rotate(get(fa)==get(x)?fa:x); tar=x; } inline int getl(int x) { while(ch[x][0]) x=ch[x][0]; return x; } inline int getr(int x) { while(ch[x][1]) x=ch[x][1]; return x; } inline void insert(int &o,double x,double y,int last) { if(!o) { o=++cnt; f[o]=last, X[o]=x,Y[o]=y; return; } insert(ch[o][x-X[o]>eps], x, y, o); } inline int getans(int x,double k) { if(!x) return 0; if(k<=lk[x]+eps&&k+eps>=rk[x]) return x; if(lk[x]<k+eps) return getans(ch[x][0], k); else return getans(ch[x][1], k); } inline void del(int x) { if(!ch[x][0]) { int right=getl(ch[x][1]); splay(right,ch[x][1]), root=right, f[root]=ch[x][1]=0; lk[root]=inf; } else if(!ch[x][1]) { int left=getr(ch[x][0]); splay(left,ch[x][0]), root=left, f[root]=ch[x][0]=0; rk[root]=-inf; } else { int left=getr(ch[x][0]),right=getl(ch[x][1]); splay(left,ch[x][0]), splay(right,ch[x][1]); root=left, f[root]=0, ch[root][1]=right, f[right]=root; rk[root]=lk[right]=slope(root,right); } } // 平衡树上查询前驱 inline int pre(int x) { int cur=ch[x][0],re=0; while(cur) { if(slope(cur,x)+eps>=rk[cur]) re=cur,cur=ch[cur][0]; else cur=ch[cur][1]; } return re; } // 平衡树上查询后继 inline int nxt(int x) { int cur=ch[x][1],re=0; while(cur) { if(slope(x,cur)<=lk[cur]+eps) re=cur,cur=ch[cur][1]; else cur=ch[cur][0]; } return re; } inline void maintain(int x) { splay(x,root); if(ch[root][0]) { int left=pre(root); if(left) { splay(left, ch[x][0]); ch[left][1]=f[ch[left][1]]=0; rk[left]=lk[x]=slope(left, x); } else lk[x]=-inf; } else lk[x]=inf; if(ch[x][1]) { int right=nxt(x); if(right) { splay(right, ch[x][1]); ch[right][0]=f[ch[right][0]]=0; rk[x]=lk[right]=slope(x, right); } else rk[x]=inf; } else rk[x]=-inf; if(lk[x]-rk[x]<=eps) del(x); } }splay; int n; ll d; struct Node { ll P,D,G,R; }t[maxn]; ll F[maxn]; bool cmp(Node i,Node j) { return i.D<j.D; } inline void work() { root=splay.cnt=0; int i,j=0; for(i=1;i<=n;++i) scanf("%lld%lld%lld%lld",&t[i].D,&t[i].P,&t[i].R,&t[i].G); t[++n].D=d+1; t[n].P=t[n].R=t[n].G=0; sort(t+1,t+1+n,cmp); ll ret=F[0], re=0; for(i=1;i<=n;++i) { F[i]=F[i-1]; if(root) { j=splay.getans(root, (long double)-1.00000*t[i].D); j=idxx[j]; re=F[j]-t[j].P+t[j].R+t[j].G*(t[i].D-t[j].D-1LL); F[i]=max(F[i-1],re); } ret=max(ret,F[i]); if(i!=n&&F[i]>=t[i].P) { splay.insert(root, (long double)t[i].G, (long double)(F[i]-t[i].P+t[i].R-t[i].G*(t[i].D+1LL)),0); idxx[splay.cnt]=i; splay.maintain(splay.cnt); } } printf("%lld\n",ret); for(int i=0;i<=splay.cnt;++i) splay.ch[i][0]=splay.ch[i][1]=splay.f[i]=splay.lk[i]=splay.rk[i]=0; root=splay.cnt=0; } int main() { for(int cas=1;;++cas) { scanf("%d%lld%lld",&n,&F[0],&d); if(n==0&&F[0]==0&&d==0) break; printf("Case %d: ",cas); work(); } return 0; }