123789456ye

已AFO

[NOI2007]货币兑换

\(dp[i],a[i],b[i],x[i],y[i]\)为第\(i\)天时的最大收益/A卷价格/B卷价格/将所有现金兑成A卷数量/B卷
于是有

\[dp[i]=\max(dp[i-1],x[j]*a[i]+y[j]*b[i]) \]

然后可以写成斜率优化形式

\[y[j]=-\frac{a[i]}{b[i]}x[j]+\frac{dp[i]}{b[i]} \]

对于每个点\((x[j],y[j])\),我们用一根斜率为\(-\frac{a[i]}{b[i]}\)的直线去经过它,使截距最大,也就是维护上凸包
因为每一点x坐标,斜率都不单调,所以上平衡树 cdq
先把所有点按k递增顺序排好,再按天数分治,使得处理时是按天数递增的顺序
因为这是dp,所以要先处理左区间,再递归右区间
在处理影响时我们不用管左区间的顺序,也就是可以按x坐标递增插入凸包里
递归出口时首先\(dp[i]=\max(dp[i],dp[i-1])\),然后再计算\(y[i]=\frac{dp[i]}{a[i]*rate[i]+b[i]},x[i]=y[i]*a[i]\)
加上归并把复杂度降低到\(O(n\log n)\)
注意这道题似乎有点卡精度(虽然我没有)

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define db double
#define eps 1e-9
#define inf 1e18
db dp[maxn];
int n, sta[maxn];
struct Cash
{
	db a, b, rate, x, y, k;
	int id;
	bool operator < (const Cash& p) const { return k < p.k; }
	friend void read(Cash& p) { scanf("%lf%lf%lf", &p.a, &p.b, &p.rate); }
}cash[maxn], tp[maxn];
db getk(int a, int b)
{
	if (fabs(cash[a].x - cash[b].x) <= eps) return inf;
	return (cash[b].y - cash[a].y) / (cash[b].x - cash[a].x);
}
void merge_sort(int l, int r, int mid)
{
	int t1 = l, t2 = mid + 1, k = l;
	while (t1 <= mid && t2 <= r)
	{
		if (cash[t1].x < cash[t2].x) tp[k++] = cash[t1++];
		else tp[k++] = cash[t2++];
	}
	while (t1 <= mid) tp[k++] = cash[t1++];
	while (t2 <= r) tp[k++] = cash[t2++];
	for (int i = l; i <= r; ++i) cash[i] = tp[i];
}
void cdq(int l, int r)
{
	if (l == r)
	{
		dp[l] = max(dp[l], dp[l - 1]);
		cash[l].y = dp[l] / (cash[l].a * cash[l].rate + cash[l].b), cash[l].x = cash[l].y * cash[l].rate;
		return;
	}
	int mid = (l + r) >> 1, top = 0;
	for (int i = l, t1 = l, t2 = mid + 1; i <= r; ++i)//先按天数排序,递归
	{
		if (cash[i].id <= mid) tp[t1++] = cash[i];
		else tp[t2++] = cash[i];
	}
	for (int i = l; i <= r; ++i) cash[i] = tp[i];
	cdq(l, mid);
	for (int i = l; i <= mid; ++i)//维护左区间的上凸包
	{
		while (top >= 2 && getk(sta[top], i) > getk(sta[top - 1], sta[top])) --top;
		sta[++top] = i;
	}
	for (int i = mid + 1; i <= r; ++i)//更新右区间答案
	{
		while (top >= 2 && getk(sta[top - 1], sta[top]) <= cash[i].k) --top;
		dp[cash[i].id] = max(dp[cash[i].id], cash[sta[top]].x * cash[i].a + cash[sta[top]].y * cash[i].b);
	}
	cdq(mid + 1, r);
	merge_sort(l, r, mid);
}
int main()
{
	scanf("%d%lf", &n, &dp[0]);
	for (int i = 1; i <= n; ++i) read(cash[i]), cash[i].k = -cash[i].a / cash[i].b, cash[i].id = i;
	sort(cash + 1, cash + n + 1); cdq(1, n);
	printf("%.3lf", dp[n]);
	return 0;
}
posted @ 2020-03-15 16:28  123789456ye  阅读(115)  评论(0编辑  收藏  举报