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$
一定要特别注意横坐标相等的情况,一定特判是无限大还是无限小
#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; 
}

  

posted @ 2019-07-08 12:49  EM-LGH  阅读(348)  评论(0编辑  收藏  举报