[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;}//completely win
		else if(wl <= xl && wr <= xr) return;//completely lose 
		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;
}
posted @ 2021-02-23 09:54  皮皮刘  阅读(73)  评论(0编辑  收藏  举报