• ●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)$
    $quadquadfrac{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进行贡献就好。

    代码:

    #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
    ",++Cas,DP[N]);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    每天问自己十个问题
    FreeBSD入门安装及汉化
    商人的10条赚钱规则
    端口扫描程序nmap使用手册
    canvas
    表单2.0
    H5数据保存之storage
    Linux基础命令2
    Linux文件权限与修改
    Linux基础命令1
  • 原文地址:https://www.cnblogs.com/zj75211/p/8148800.html
Copyright © 2020-2023  润新知