BZOJ 3963: [WF2011]MachineWorks 斜率优化 + splay动态维护凸包

Code: 

#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; 
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值