• 题解 货币兑换


    题目传送门

    题目大意

    (n)天,每天都是同样的两只股票。每天都有三个参数(A_i,B_i,R_i),表示当天股票价格,以及买入的(A,B)两股数量之比为(R_i:1)

    提示:

    一定存在最优方案使得每天要么不动,要么全部卖出

    思路

    这道题拖了好久啊!!!主要是以前对斜率优化不是特别理解(尽管可能以后看来现在的我也不是很理解)(大雾

    提示似乎已经写得很清楚了,如果没有这个提示的话估计这个贪心都没办法猜到吧。

    我们可以设(f_i)为第(i)天卖出交易之后收获的最大( exttt{money}),于是我们可以得到( exttt{dp})转移式:

    [f_i=max{x_j A_i+y_jB_i} ]

    其中(x_j,y_j)表示的是第(j)天把所有钱换做股票的(A,B)两股的数量。可以得到(x_j=dfrac{f_jR_j}{A_jR_j+B_j},y_j=dfrac{f_j}{A_jR_j+B_j})

    于是,我们就可以得到:

    [y_j=-frac{A_i}{B_i}x_j+frac{f_i}{B_i} ]

    如果我们设一条经过原点,斜率为(-dfrac{A_i}{B_i})的直线为(Q),于是,我们要求的就是用(Q)去切,使截距最大的((x_j,y_j))。(截距就是当(x=x_j)(y_j)与函数值差的绝对值,不理解见下图)

    我们可以发现的是,如果当前点与左边点的斜率比(Q)的斜率小,那把当前点移到左边点,那么答案肯定更优。类似的,如果当前点与右边点的斜率比(Q)的斜率大,那把当前点移到右边点,那么答案肯定更优。大意如下图:

    于是,我们就可以得到,我们需要维护的其实是个凸包,最优的答案就在凸包上。(这个自己想一下就可以明白了)这个东西有(2)种维护方法,一种是( exttt{Splay}),另外一种是( exttt{cdq})分治,我采用的前者。

    首先很显然的是,( exttt{Splay})内部以(x)坐标为关键字。

    很显然,我们查询的答案的话直接按照上面的方法暴力走就好了,因为凸包的斜率单调递减,又因为( exttt{Splay})的深度是(log n)级别的,所以此操作单次是(log n)的。

    我们考虑插入,很显然我们需要找到可以构成凸包的最远左右端点。大意如下图:

    updated on 2020-07-15:

    其实不是最远左右端点,而是最近的可以构成左右端点的点。

    很显然,我们直接把中间的点直接删完即可。

    找到最远左右端点直接在( exttt{Splay})上查找即可,单次时间复杂度为(log n)

    不过需要注意的是,我们还需要把插入点在凸包内的情况排除掉。不过这道题数据很水,即使不判也不会有什么问题。

    具体实现的话,直接( exttt{Splay})里面维护每个凸包上的点在凸包上与左右两端的点的斜率。

    这道题也很卡精度,所以尽量不要卡得很严,大于小于的时候多加几个( ext{eps})

    综上,总时间复杂度为(Theta(nlog n))

    话说斜率优化不就是保证(x)坐标单调上升的版本么?(雾

    ( ext {Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define MAXN 100005
    #define eps 1e-9
    #define inf 1e9
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    int n,rt,fa[MAXN],son[MAXN][2];
    double A[MAXN],B[MAXN],R[MAXN],X[MAXN],Y[MAXN],lk[MAXN],rk[MAXN],dp[MAXN];
    
    bool rnk (int x){return son[fa[x]][1] == x;}
    
    void rotate (int x,int &root){
    	int y = fa[x],z = fa[y],k = rnk (x),w = son[x][!k];
    	if (y == root) root = x;else son[z][rnk (y)] = x;
    	son[x][!k] = y,son[y][k] = w,fa[w] = y,fa[x] = z,fa[y] = x;
    }
    
    void Splay (int x,int &root){
    	while (x ^ root){
    		int y = fa[x];
    		if (y ^ root) rotate (rnk (x) == rnk (y) ? y : x,root);
    		rotate (x,root);
    	}
    }
    
    int find (int x,double Slope){
    	if (!x) return 0; 
    	if (lk[x] + eps >= Slope && rk[x] <= Slope + eps) return x;
    	else if (lk[x] < Slope + eps) return find (son[x][0],Slope);
    	else return find (son[x][1],Slope);
    }
    
    double GetSlp (int a,int b){
    	if (X[a] - X[b] > -eps && X[a] - X[b] < eps) return -inf;
    	return (Y[b] - Y[a]) / (X[b] - X[a]);
    }
    
    int pre (int x){
    	int y = son[x][0],ans = y;
    	while (y){
    		if (lk[y] + eps >= GetSlp (y,x)) ans = y,y = son[y][1];
    		else y = son[y][0];
    	}
    	return ans;
    }
    
    int suf (int x){
    	int y = son[x][1],ans = y;
    	while (y){
    		if (rk[y] <= GetSlp (x,y) + eps) ans = y,y = son[y][0];
    		else y = son[y][1];
    	}
    	return ans;
    }
    
    void ins (int &x,int las,int id){
    	if (!x) return x = id,fa[x] = las,void ();
    	if (X[id] <= X[x] + eps) ins (son[x][0],x,id);
    	else ins (son[x][1],x,id);
    }
    
    void Rebuild (int x){
    	Splay (x,rt);
    	if (son[x][0]){
    		int k = pre (x);
    		Splay (k,son[x][0]),son[k][1] = 0,rk[k] = lk[x] = GetSlp (k,x);
    	}
    	else lk[x] = inf;
    	if (son[x][1]){
    		int k = suf (x);
    		Splay (k,son[x][1]),son[k][0] = 0,lk[k] = rk[x] = GetSlp (x,k);
    	}
    	else rk[x] = -inf;
    	if (lk[x] <= rk[x] + eps){
    		rt = son[x][0],son[rt][1] = son[x][1],fa[son[rt][1]] = rt,fa[rt] = 0;
    		rk[rt] = lk[son[rt][1]] = GetSlp (rt,son[rt][1]);
    	}
    }
    
    signed main(){
    	scanf ("%d%lf",&n,&dp[0]);
    	for (Int i = 1;i <= n;++ i){
    		scanf ("%lf%lf%lf",&A[i],&B[i],&R[i]);int best = find (rt,-A[i] / B[i]);
    		dp[i] = max (dp[i - 1],X[best] * A[i] + Y[best] * B[i]);
    		Y[i] = dp[i] / (A[i] * R[i] + B[i]),X[i] = Y[i] * R[i];
    		ins (rt,0,i),Rebuild (i);
    	}
    	printf ("%.3f
    ",dp[n]);
    	return 0;
    }
    

    updated on 2020-07-15:

    其实上面的代码是有问题。我们在删去凸包里面的点的时候有可能删掉不该删的点,比如:

    我们在删除掉( exttt {H})的时候会顺带删掉原凸包上的( exttt{C}),但这显然不合法。但是这道题的数据里面根本就没有点在凸包里面的情况,所以还是草草过掉了。

  • 相关阅读:
    Linux基础知识
    oracle用户及表空间基础
    渗透测试之目录扫描-Dirbuster
    oracle自定义函数身份证15位和18位的转换
    linux 网络带宽和延时测试
    LNMP(linux+nginx+mysql+php)服务器环境配置
    使用Medusa美杜莎暴力破解SSH密码
    暴力密码在线破解工具
    在linux下搭建NFS服务器实现文件共享
    Nginx是做什么的
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13301970.html
Copyright © 2020-2023  润新知