luogu P4027 [NOI2007]货币兑换

luogu

ws**

这里设\(f_i\)为前\(i\)天内得到的最多钱数,转移就考虑在第\(i\)天把钱变成券,然后枚举\(>i\)\(j\),在第\(j\)天换成钱,这里要算出第\(i\)天换的B券数量,直接写转移\(f_j=\max(f_{j-1},\max_{i<j} \frac{f_i}{a_ir_i+b_i}(a_jr_i+b_j))\)

考虑\(\frac{f_i}{a_ir_i+b_i}(a_jr_i+b_j)\to f_j\),先改成\(\frac{f_ir_i}{a_ir_i+b_i}*a_j+\frac{f_i}{a_ir_i+b_i}*b_j\to f_j\),然后设\(x_i=\frac{f_ir_i}{a_ir_i+b_i},y_i=\frac{f_i}{a_ir_i+b_i}\),原式改写成\(x_ia_j+y_ib_j\to f_j\)

然后把\(y_i\)提出,得\(y_i=-\frac{a_j}{b_j}x_i+\frac{1}{b_j}f_j\),所以\(f_j\)可以看做是对于点\((x_i,y_i)\),用斜率\(k=-\frac{a_j}{b_j}\)去经过这个点,得到的最大的截距的\(/b_i\)的值.所以如果把前面的所有\((x_i,y_i)\)的上凸壳搞出来,那截距最大的\((x_i,y_i)\)会满足在它前面的点之间的线段斜率\(>-\frac{a_j}{b_j}\),后面的线段斜率\(\le-\frac{a_j}{b_j}\)(可以画图理解),那么维护好凸壳后就可以每次\(O(logn)\)转移了.由于凸壳一定是由一些横坐标不相同的点依次排列形成,所以可以搞一个以横坐标为关键字的平衡树,每次把新的点插到对应位置,然后用叉积删掉这个点的不合法前驱后继,使得凸包没有凹下去的地方

不想写平衡树可以写multiset,就是查询大概要两个log

#include<bits/stdc++.h>
#define LL long long
#define db double

using namespace std;
const int N=1e5+10;
const db eps=1e-10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return x*w;
}
db f[N];
struct node
{
	db x,y;
	bool operator < (const node &bb) const {return x<bb.x;}
	node operator - (const node &bb) const {return (node){x-bb.x,y-bb.y};}
	db operator * (const node &bb) const {return x*bb.y-bb.x*y;}
}nw;
db K(node aa,node bb){return (bb.y-aa.y)/(bb.x-aa.x);}
multiset<node> sb;
multiset<node>::iterator it,nt,n2;
int n;

int main()
{
    ///
	n=rd(),f[1]=rd();
	for(int i=1;i<=n;++i)
	{
		db a,b,c;
		scanf("%lf%lf%lf",&a,&b,&c);
		f[i]=max(f[i],f[i-1]);
		if(!sb.empty())
		{
			it=sb.begin(),f[i]=max(f[i],((*it).y+(*it).x*a/b)*b);
			it=--sb.end(),f[i]=max(f[i],((*it).y+(*it).x*a/b)*b);
			db l=(*sb.begin()).x+eps,r=(*(--sb.end())).x-eps,ls=1e9;
			while(r-l>eps&&r-l<ls)
			{
				ls=r-l;
				db md=(l+r)/2;
				it=sb.upper_bound((node){md,0});
				if(it==sb.end()) break;
				nt=--it,++it;
				f[i]=max(f[i],max((*it).y+(*it).x*a/b,(*nt).y+(*nt).x*a/b)*b);
				if(K(*it,*nt)>-a/b) l=(*it).x+eps;
				else r=(*nt).x-eps;
			}
		}
		nw=(node){f[i]/(a*c+b)*c,f[i]/(a*c+b)};
		it=sb.insert(nw);
		if(it!=sb.begin()&&it!=(--sb.end()))
		{
			nt=n2=it,--nt,++n2;
			if(((*it)-(*nt))*((*n2)-(*it))>eps){sb.erase(it);continue;}
		}
		if(it!=sb.begin())
		{
			nt=it,--nt;
			if(fabs((*it).x-(*nt).x)<eps)
			{
				if((*it).y<(*nt).y){sb.erase(it);continue;}
				sb.erase(nt);
			}
		}
		if(it!=(--sb.end()))
		{
			n2=it,++n2;
			if(fabs((*it).x-(*n2).x)<eps)
			{
				if((*it).y<(*n2).y){sb.erase(it);continue;}
				sb.erase(n2);
			}
		}
		while(it!=sb.begin()&&it!=(++sb.begin()))
		{
			nt=it,--nt;
			n2=nt,--n2;
			if(((*nt)-(*it))*((*n2)-(*nt))>eps) break;
			sb.erase(nt);
		}
		while(it!=(--sb.end())&&it!=(--(--sb.end())))
		{
			nt=it,++nt;
			n2=nt,++n2;
			if(((*nt)-(*it))*((*n2)-(*nt))<-eps) break;
			sb.erase(nt);
		}
	}
	printf("%.3lf\n",f[n]);
	return 0;
}
posted @ 2020-04-17 21:04  ✡smy✡  阅读(180)  评论(0编辑  收藏  举报