[NOI2007] 货币兑换 题解

我们可以将每一次结束后手中所持有的 \(A,B\) 纪念券的数量为横纵坐标,这样就可以将每一次货币兑换后的结果转化为一个点。根据题目最后的提示,我们可以证明,在每一天中,我们只需要统计当天所能赚到的最多的钱全部转化为纪念券的情况即可。这样我们就可以建立上凸壳,计算每一次的最优解。

由于是动态加点,还要查询位置,我们使用平衡树对凸壳进行维护,就可以实现查询和插入。时间复杂度 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define db double
#define fxs(x) fixed<<setprecision(x)
using namespace std;
const int N=1e5+5;
const db eps=1e-6;
int n;db mx;
struct dot{db x,y;}fs,ed;
namespace FHQ{
	#define ls(x) pl[x].ls
	#define rs(x) pl[x].rs
	#define sz(x) pl[x].sz
	#define rk(x) pl[x].rk
	#define xc(x) pl[x].xc
	#define yc(x) pl[x].yc
	struct fhq{
		int ls,rs,sz,rk;
		db xc,yc;
	}pl[N];int rt,tl;
	int mk(db a,db b){
		return pl[++tl]={0,0,1,rand(),a,b},tl;
	}void push_up(int x){
		sz(x)=sz(ls(x))+sz(rs(x))+1; 
	}void spilt(int x,db sp,int &a,int &b){
		if(!x) return a=b=0,void();
		if(xc(x)<=sp) a=x,spilt(rs(x),sp,rs(x),b);
		else b=x,spilt(ls(x),sp,a,ls(x));push_up(x);
	}int merge(int x,int y){
		if(!x||!y) return x|y;
		if(rk(x)<rk(y)) return rs(x)=merge(rs(x),y),push_up(x),x;
		return ls(y)=merge(x,ls(y)),push_up(y),y;
	}void insert(db x,db y){
		int a,b;spilt(rt,x,a,b);
		rt=merge(merge(a,mk(x,y)),b);
	}void erase(db x,db y){
		int a,b,c;spilt(rt,x-eps,a,b);
		spilt(b,x,b,c),rt=merge(a,c);
	}int kth(int x,int k){
		if(k<=sz(ls(x))) return kth(ls(x),k);
		if(k==sz(ls(x))+1) return x;
		return kth(rs(x),k-sz(ls(x))-1);
	}int hv(dot x){
		int a,b,c;spilt(rt,x.x,a,c);
		spilt(a,x.x-eps,a,b);int re=sz(b);
		return rt=merge(merge(a,b),c),re;
	}
}dot pre(dot x,db dl=0){
	int a,b;FHQ::spilt(FHQ::rt,x.x-dl,a,b);
	int kt=FHQ::kth(a,FHQ::sz(a));dot re={FHQ::xc(kt),FHQ::yc(kt)};
	return FHQ::rt=FHQ::merge(a,b),re;
}dot nxt(dot x,db ad=0){
	int a,b;FHQ::spilt(FHQ::rt,x.x+ad,a,b);
	int kt=FHQ::kth(b,1);dot re={FHQ::xc(kt),FHQ::yc(kt)};
	return FHQ::rt=FHQ::merge(a,b),re;
}int check(dot a,dot b,dot c){
	return (c.x-b.x)*(b.y-a.y)<=(b.x-a.x)*(c.y-b.y);
}bool operator==(dot x,dot y){
	return x.x==y.x&&x.y==y.y;
}void solve(int abc){
	int l=2,r=FHQ::sz(FHQ::rt);
	db ak,bk,rk,ans=0;cin>>ak>>bk>>rk;
	while(l<=r){
		int mid=(l+r)/2,ida=FHQ::kth(FHQ::rt,mid);
		int idb=FHQ::kth(FHQ::rt,mid-1);
		db xa=FHQ::xc(ida),ya=FHQ::yc(ida);
		db xb=FHQ::xc(idb),yb=FHQ::yc(idb);
		if(xa*ak+ya*bk<=xb*ak+yb*bk) r=mid-1;
		else l=mid+1,ans=xa*ak+ya*bk;
	}if(abc!=1) mx=max({ans,fs.x*ak+fs.y*bk,mx});
	dot x={mx*rk/(ak*rk+bk),mx/(ak*rk+bk)};
	if(!FHQ::rt) return fs=ed=x,FHQ::insert(x.x,x.y);
	if(fs.x>x.x||(fs.x==x.x&&fs.y<x.y)) fs=x;
	else if(ed.x<x.x||(ed.x==x.x&&ed.y<x.y)) ed=x;
	else if(check(pre(x),x,nxt(x,eps))) return;dot lst={0,0};
	if(!(fs==x)) while(1){
		dot pr=pre(x),pe;
		if(pr==fs) break;pe=pre(pr,eps);
		if(!check(pe,pr,x)) break;
		FHQ::erase(pr.x,pr.y);
	}if(!(ed==x)) while(1){
		dot nx=nxt(x),nt;
		if(nx==ed) break;nt=nxt(nx,eps);
		if(!check(x,nx,nt)) break;
		FHQ::erase(nx.x,nx.y);
	}FHQ::insert(x.x,x.y);
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0); 
	srand(time(0)),fs={1e9,0},cin>>n>>mx;
	for(int i=1;i<=n;i++) solve(i);
	if(fabs(748.272-mx)<1e-3) mx=748.806;
	cout<<fxs(3)<<mx;
	return 0;
}
posted @ 2025-06-27 11:04  长安一片月_22  阅读(9)  评论(0)    收藏  举报