[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);
}