[NOI2007] 货币兑换
作者:互联网
前言
妙啊,我最喜欢码农题偷懒了。
题目
讲解
思路来源:panyf orz
首先 \(O(n^2)\) 的 \(dp\) 很好想,令 \(f_i\) 为第 \(i\) 天最多获得的钱,则有:
\(f_i=\max\{\frac{f_j*Rate_ja_i}{Rate_ja_j+b_j}+\frac{f_j*b_i}{Rate_ja_j+b_j}\}\)
令 \(A_j=\frac{f_j*Rate_j}{Rate_ja_j+b_j},B_j=\frac{f_j}{Rate_ja_j+b_j}\)
\(f_i=\max\{A_ja_i+B_jb_i\}=b_i\max\{A_j\frac{a_i}{b_i}+B_j\}\)
哦,后面的 \(A_j\frac{a_i}{b_i}+B_j\) 不就是一次函数 \(kx+b\) 的形式吗?!
李超线段树!!!
为防止精度误差,要对 \(\frac{a_i}{b_i}\) 离散化,而且这样常数也会小一些,不需要浮点数动态开点。
时间复杂度 \(O(nlog^2n)\)。
其实可以优化一下,因为我们发现本题中我们插入的线段定义域都是 \(x\in[1,n]\)(离散化后),所以我们在插入的时候一定不会走两边,可以少掉一个 \(log\)。
还有一个\(\color{white}{小}\)优化是在询问时如果一个区间中没有线段,那么下面一定也没有,直接返回,与上一个优化同理。
Query没返回值还能过40pts是我没想到的。
代码
const int MAXN = 100005;
int n;
double S,a[MAXN],b[MAXN],r[MAXN],lsh[MAXN];
struct line
{
double k,b;
bool f;//whether it exist or not
line(){}
line(double k1,double b1,bool f1){
k = k1;
b = b1;
f = f1;
}
};
double getf(line l,int x){return l.k * lsh[x] + l.b;}
#define lc (x<<1)
#define rc (x<<1|1)
struct LiChaoSegmentTree
{
line t[MAXN << 2];
void Add_Line(int x,int l,int r,line w)
{
int mid = (l+r) >> 1;
if(!t[x].f) {t[x] = w;return;}
double xl = getf(t[x],l),xr = getf(t[x],r),wl = getf(w,l),wr = getf(w,r);
if(wl >= xl && wr >= xr) {swap(t[x],w);return;}//win easily
else if(wl <= xl && wr <= xr) return;//lose easily
else//worthy opponent
{
if(getf(w,mid) >= getf(t[x],mid)) swap(t[x],w);
//if(l == r) return ; boundary (It's unnecessary.)
if(getf(w,l) > getf(t[x],l)) Add_Line(lc,l,mid,w);
else Add_Line(rc,mid+1,r,w);
return ;
}
}
double Query(int x,int l,int r,int pos)
{
if(!t[x].f) return 0;//Especially suitable for this subject,for its segments all exist in [1,n].
double ret = getf(t[x],pos);
if(l == r) return ret;
int mid = (l+r) >> 1;
if(pos <= mid) ret = Max(ret,Query(lc,l,mid,pos));
else ret = Max(ret,Query(rc,mid+1,r,pos));
return ret;
}
}st;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%d %lf",&n,&S);
for(int i = 1;i <= n;++ i)
{
scanf("%lf %lf %lf",&a[i],&b[i],&r[i]);
lsh[i] = a[i] / b[i];
}
sort(lsh+1,lsh+n+1);
for(int i = 1;i <= n;++ i)
{
int x = lower_bound(lsh+1,lsh+n+1,a[i]/b[i]) - lsh;
S = Max(S,b[i] * st.Query(1,1,n,x));
double K = S * r[i] / (r[i] * a[i] + b[i]),B = S / (r[i] * a[i] + b[i]);
st.Add_Line(1,1,n,line(K,B,1));
}
printf("%.3f",S);
return 0;
}
标签:return,int,double,getf,NOI2007,兑换,货币,frac,ja 来源: https://www.cnblogs.com/PPLPPL/p/14434310.html