首页 > 其他分享 >P4027 [NOI2007] 货币兑换

P4027 [NOI2007] 货币兑换

时间:2022-11-17 12:55:38浏览次数:59  
标签:P4027 tree mid times NOI2007 兑换 ans calc id

前言

打完这道题,感觉对李超线段树又有了进一步的了解。

分析

一个明显的性质,如果要买卷或卖卷的话,那么一定是全买全卖的,显然。

设 \(ans_i\) 为第 \(i\) 天拥有的最大钱数, \(x_i\) 为第 \(i\) 天用 \(ans_i\) 可以兑换的 A 卷数, \(y_j\) 为兑换的 B 卷数。

则有 \(x_i=\dfrac{ans_i\times Rate_i}{Rate_i\times A_i+B_i}\),\(y_i=\dfrac{ans_i}{Rate_i\times A_i+B_I}\)。

若第 \(i\) 天不卖,那么 \(ans_i=ans_{i-1}\) 。

若第 \(i\) 天卖出卷,那么 \(ans_i=\max\{A_i\times x_j+B_i\times y_j\}\)。

变形得,\(ans_i=B_i\times (\dfrac{A_i}{B_i}\times x_j+y_j)\)。

设 \(k=x_j\),\(x=\dfrac{A_i}{B_i}\),\(b=y_j\),用李超线段树维护即可。

注意到 \(x\) 的值可能为小数,那就不能用李超了吗?我们可以离散化,再映射回去即可。

\(code\)

#include<bits/stdc++.h>	
#define N 100005
#define db double 
#define ls u<<1
#define rs u<<1|1
#define eps 1e-6
using namespace std;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n;
int tree[N<<2];
db ans;
db A[N],B[N],R[N],C[N],D[N],x[N],y[N];
db calc(int pos,int id){return D[pos]*x[id]+y[id];}
db Query(int u,int l,int r,int X){
	if(l==r) return calc(X,tree[u]);
	int mid=(l+r)>>1;
	db res=calc(X,tree[u]);
	if(X<=mid) res=max(res,Query(ls,l,mid,X));
	else res=max(res,Query(rs,mid+1,r,X));
	return res;
}
void UpDate(int u,int l,int r,int id){
	if(calc(l,id)>calc(l,tree[u])&&calc(r,id)>calc(r,tree[u])) return void(tree[u]=id);
	else if(calc(l,id)<=calc(l,tree[u])&&calc(r,id)<=calc(r,tree[u])) return ;
	int mid=(l+r)>>1;
	if(x[id]+eps>x[tree[u]]){
		if(calc(mid,id)+eps>calc(mid,tree[u])) UpDate(ls,l,mid,tree[u]),tree[u]=id;
		else UpDate(rs,mid+1,r,id);
	}else{
		if(calc(mid,id)>calc(mid,tree[u])+eps) UpDate(rs,mid+1,r,tree[u]),tree[u]=id;
		else UpDate(ls,l,mid,id);
	}
}
signed main(){
	n=read(),scanf("%lf",&ans);
	for(int i=1;i<=n;++i){
		scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
		C[i]=D[i]=A[i]/B[i];
	}
	sort(D+1,D+1+n);
	for(int i=1;i<=n;++i){
		int X=lower_bound(D+1,D+1+n,C[i])-D;
		ans=max(ans,B[i]*Query(1,1,n,X));
		x[i]=R[i]*ans/(R[i]*A[i]+B[i]),y[i]=ans/(R[i]*A[i]+B[i]);
		UpDate(1,1,n,i);
	}
	printf("%.3lf\n",ans);
	return 0;
}

标签:P4027,tree,mid,times,NOI2007,兑换,ans,calc,id
From: https://www.cnblogs.com/jiangchenyyds/p/16899121.html

相关文章

  • 322. 零钱兑换 ---- 动态规划
    给你一个整数数组coins,表示不同面额的硬币;以及一个整数amount,表示总金额。计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,......
  • P3188 [HNOI2007]梦幻岛宝珠 题解
    一道不错的dp题,下令原背包容量为\(m\)。注意到\(w_i=a\times2^b\)中\(a,b\)都比较小,尝试按照\(a\)或者\(b\)分组然后合并,但是显然如果我们按照\(a\)分组就......
  • 代码随想录day45 | 70. 爬楼梯 322. 零钱兑换 279. 完全平方数
    70.爬楼梯题目|文章思路这道题目要求有序,因此是全背包的排列做法。1.数组下标以及含义dp[i]:爬到n台阶一共有dp[i]种方法。2.递推关系dp[i]+=dp[i-j];3.初始......
  • 代码随想录day44 | 完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ
    完全背包文章思路有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物......
  • 动态规划-零钱兑换
    零钱兑换也是动态规划的典型问题,一般是给你几种零钱,数量不限,给一个amount,问共有多少种兑零钱的方法。我们看一个案例案例1:给你一个整数数组coins,表示不同面额的硬币;以......
  • LC322---零钱兑换
    ​​322.零钱兑换​​难度中等1087给定不同面额的硬币​​coins​​​和一个总金额​​amount​​​。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没......
  • BZOJ 1185([HNOI2007]最小矩形覆盖-旋转卡壳+点集几何意义)
    1185:[HNOI2007]最小矩形覆盖TimeLimit: 10Sec  MemoryLimit: 162MBSec  SpecialJudgeSubmit: 258  Solved: 137Description l要事先改成......
  • BZOJ 1189([HNOI2007]紧急疏散evacuate-网络流二分+拆点)
    发生了火警,所有人员需要紧急疏散!假设每个房间是一个NM的矩形区域。每个格子如果是’.’,那么表示这是一块空地;如果是’X’,那么表示这是一面墙,如果是’D’,那么表示这是一扇门......
  • [HNOI2007]紧急疏散EVACUATE
    题目传送门:[HNOI2007]紧急疏散EVACUATEbfs+二分+最大流#include<queue>#include<string>#include<cstdio>#include<cstring>#include<algorithm>constintN=2......
  • 零钱兑换问题
    零钱兑换问题作者:Grey原文地址:博客园:零钱兑换问题CSDN:零钱兑换问题题目描述给你一个整数数组coins,表示不同面额的硬币;以及一个整数amount,表示总金额。计算并返......