zoukankan      html  css  js  c++  java
  • BZOJ 1492 [NOI2007]货币兑换Cash:斜率优化dp + cdq分治

    传送门

    题意

    初始时你有 $ s $ 元,接下来有 $ n $ 天。

    在第 $ i $ 天,A券的价值为 $ A[i] $ ,B券的价值为 $ B[i] $ 。

    在第 $ i $ 天,你可以进行两种操作:

    • 卖出:将 $ %OP $ 的A券和 $ %OP $ 的B券兑换成人民币,其中 $ OP $ 为 $ [0,100] $ 之间的任意实数
    • 买入:支付 $ IP $ 元,买入A、B券的总价值为 $ IP $ 元,且买入A、B券的数量之比为 $ Rate[i] $

    人民币和金券的数量可以为一个实数。一天可以进行多次操作。

    问你 $ n $ 天后最多能获得多少元钱。

    $ (n leq 10^5, 0 leq A[i],B[i] leq 10, 0 leq Rate[i] leq 100, ans leq 10^9) $

    题解

    首先有一个显然的结论:必定有一种最优方案,满足每一天至多进行一种操作。

    设 $ f[i] $ 表示在第 $ i $ 天能够获得的最大钱数,$ x[i], y[i] $ 分别表示第 $ i $ 天能够获得A、B券的最大数量。

    则有:

    [x[i] = frac{f[i]}{A[i]+B[i]/Rate[i]} ]

    [y[i] = frac{f[i]}{A[i]*Rate[i]+B[i]} ]

    然后考虑 $ f[i] $ 如何转移。

    对于第 $ i $ 天来说,要么什么都不做,要么将之前某一天的金券在这一天卖掉。

    所以:

    [f[i] = max(f[i-1], x[j]*A[i]+y[j]*B[i]) quad (1 leq j<i) ]

    然而朴素dp是 $ O(n^2) $ 的过不了……

    先让所有的 $ f[i] = max(f[i],f[i-1]) $

    那么剩下的就是 $ f[i] = max(x[j] ast A[i]+y[j] ast B[i]) quad (1 leq j<i) $

    变形得:

    [y[j] = -frac{A[i]}{B[i]}*x[j] + frac{f[i]}{B[i]} ]

    上式是一条斜率为 $ -dfrac{A[i]}{B[i]} ​$ 且过点 $ (x[j], y[j]) ​$ 的直线的斜截式。

    由于我们想让 $ f[i] $ 最大,并且因为 $ B[i] geq 0 $ ,所以只要让上式的截距 $ dfrac{f[i]}{B[i]} $ 最大即可。

    我们考虑用cdq转移dp。

    首先对于当前区间,按照 $ id $ 大小分成两个区间。$ id $ 小的在左区间,否则在右区间,且分别在左右区间中 $ id $ 大小递增(满足了 $ id $ 小的更新 $ id $ 大的的答案)。

    假设左边递归处理后是以 $ x,y $ 分别为第一和第二关键字从小到大排好序的,右边提前按照斜率 $ -dfrac{A[i]}{B[i]} $ 从大到小排好序的。

    对于左区间,我们可以先for循环扫一遍左区间求出一个上凸壳。

    那么右区间每个元素的答案,一定是取它与左边凸壳相切时的截距(因为要让截距最大)。

    并且在依次求出右区间答案时,切点是不断向右移动的(因为右区间中元素的斜率不断减小)。

    所以总复杂度就降为了 $ O(nlogn) $

    AC Code

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #define MAX_N 100005
    #define INF_LF 1e13
    #define EPS 1e-8
    
    using namespace std;
    
    struct Data
    {
    	double a,b,r,x,y,k;
    	int id;
    	friend bool operator < (const Data &a,const Data &b)
    	{
    		return a.k>b.k;
    	}
    };
    
    int n;
    double f[MAX_N];
    Data t[MAX_N];
    Data c[MAX_N];
    Data tmp[MAX_N];
    
    inline double fabs(double x)
    {
    	return x>0 ? x : -x;
    }
    
    inline bool eq(double x,double y)
    {
    	return fabs(x-y)<=EPS;
    }
    
    inline double slope(Data a,Data b)
    {
    	if(eq(a.x,b.x)) return INF_LF;
    	return (a.y-b.y)/(a.x-b.x);
    }
    
    inline bool cmp(Data a,Data b)
    {
    	if(a.x!=b.x) return a.x<b.x;
    	return a.y<b.y;
    }
    
    void read()
    {
    	scanf("%d%lf",&n,&f[1]);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lf%lf%lf",&t[i].a,&t[i].b,&t[i].r);
    		t[i].k=(eq(t[i].b,0) ? -INF_LF : -t[i].a/t[i].b);
    		t[i].id=i;
    	}
    }
    
    void cdq(int l,int r)
    {
    	if(l==r)
    	{
    		int id=t[l].id;
    		f[id]=max(f[id],f[id-1]);
    		t[l].x=f[id]/(t[l].a+t[l].b/t[l].r);
    		t[l].y=f[id]/(t[l].a*t[l].r+t[l].b);
    		return;
    	}
    	int mid=(l+r)>>1,tot=0;
    	for(int i=l,j=mid+1,k=l;k<=r;k++)
    	{
    		if(t[k].id<=mid) tmp[i++]=t[k];
    		else tmp[j++]=t[k];
    	}
    	for(int i=l;i<=r;i++) t[i]=tmp[i];
    	cdq(l,mid);
    	for(int i=l;i<=mid;i++)
    	{
    		while(tot>=2 && slope(c[tot-1],c[tot])<slope(c[tot],t[i])) tot--;
    		c[++tot]=t[i];
    	}
    	int p=1;
    	for(int i=mid+1;i<=r;i++)
    	{
    		while(p<tot && slope(c[p],c[p+1])>t[i].k) p++;
    		f[t[i].id]=max(f[t[i].id],c[p].x*t[i].a+c[p].y*t[i].b);
    	}
    	cdq(mid+1,r);
    	for(int i=l,j=mid+1,k=l;k<=r;k++)
    	{
    		if((i<=mid && cmp(t[i],t[j])) || j>r) tmp[k]=t[i++];
    		else tmp[k]=t[j++];
    	}
    	for(int i=l;i<=r;i++) t[i]=tmp[i];
    }
    
    void work()
    {
    	sort(t+1,t+1+n);
    	cdq(1,n);
    	printf("%.3f
    ",f[n]);
    }
    
    int main()
    {
    	read();
    	work();
    }
    
  • 相关阅读:
    flutter开发环境的搭建
    创建一个android项目
    android studio 安装与配置
    sentinel-dashboard.jar 安装
    三:nacos的配置中心
    二:nacos 的服务注册
    spring boot 在windows下的 批文件部署
    一:nacos 的安装与启动方式
    mysql 命令行安装方式
    Git 出现 Permission denied 时,重新生成ssh密钥
  • 原文地址:https://www.cnblogs.com/Leohh/p/9096499.html
Copyright © 2011-2022 走看看