[NOI2007] 货币兑换

一、题目

点此看题

二、解法

对斜率优化的理解还是有点问题,所以来写这篇博客了。

(f(i)) 表示到第 (i) 天最多得到多少钱,第 (i) 天能买的金券分别是:

[x_i=frac{f(i)rate_i}{a_irate_i+b_i},y_i=frac{f(i)}{a_irate_i+b_i} ]

首先可以直接不操作用 (f(i-1)),否则枚举上一次在哪一天买金券,有下列转移:

[f(i)=max(a_ix_j+b_iy_j) ]

然后就对着上面那个转移想了半天,我们用斜率优化的话要把转移写成一个关于 (i) 变量的一次函数形式。但是上面的转移却又两个与 (i) 有关的变量,所以我们把 (b_i) 拿出去就只剩下一个了:

[f(i)=b_imax(x_jcdotfrac{a_i}{b_i}+y_j) ]

(a_i/b_i) 离散化就可以用李超树了,时间复杂度 (O(nlog n))

还有,李超树下传的时候是和本层的比较。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 100005;
#define db double
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n;db f,A[M],B[M],c[M],d[M],r[M],k[4*M],b[4*M];
db calc(int x,int i)
{
	return k[i]*c[x]+b[i];
}
void ins(int i,int l,int r,db k1,db b1)
{
	int mid=(l+r)>>1;
	if(calc(mid,i)<k1*c[mid]+b1)
		swap(k[i],k1),swap(b[i],b1);
	if(l==r) return ;
	if(calc(l,i)<k1*c[l]+b1)//fuck
		ins(i<<1,l,mid,k1,b1);
	if(calc(r,i)<k1*c[r]+b1)//fuck
		ins(i<<1|1,mid+1,r,k1,b1);
}
db ask(int i,int l,int r,int x)
{
	int mid=(l+r)>>1;
	db res=calc(x,i);
	if(l==r) return res;
	if(x<=mid) res=max(res,ask(i<<1,l,mid,x));
	else res=max(res,ask(i<<1|1,mid+1,r,x));
	return res;
}
signed main()
{
	scanf("%d %lf",&n,&f);
	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(c+1,c+1+n);
	for(int i=1;i<=n;i++)
	{
		int t=lower_bound(c+1,c+1+n,d[i])-c;
		f=max(f,B[i]*ask(1,1,n,t));//询问点值
		db yi=f/(A[i]*r[i]+B[i]);
		ins(1,1,n,yi*r[i],yi);
	}
	printf("%.3f
",f);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/14520530.html