[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_i\max(x_j\cdot\frac{a_i}{b_i}+y_j) \]

\(a_i/b_i\) 离散化就可以用李超树了,时间复杂度 \(O(n\log 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\n",f);
}
posted @ 2021-03-11 21:07  C202044zxy  阅读(47)  评论(0编辑  收藏  举报