P4027 [NOI2007]货币兑换

题目链接

题不难,但是经典。

容易推得DP式子:

\[f_i=MAX(A_ig_j+B_ih_j) \]

其中

\[g_j=\frac{R_jf_j}{A_jR_j+B_j} \]

\[h_j=\frac{f_j}{A_jR_j+B_j} \]

容易想到:

\[f_i/A_i=g_j+\frac{B_i}{A_i}h_j \]

转化为一维斜率优化的基本形式。

然后我们发现,凸包的横坐标和询问的斜率都不是单调的!

怎么办?动态凸包?平衡树难写难调常数爆炸?

这时候,我们可以考虑CDQ分治。与 购票 一题类似,先递归左边,再考虑左对右的贡献,最后递归右边。

复杂度:\(O(nlogn)\)

关键代码:

...
void cdq(int l, int r) {
	if (l == r) {
		if (l)	MAX(f[l], f[l - 1]);
		return ;
	}
	int mid = (l + r) >> 1;
	cdq(l, mid);
	int tot = 0;
	for (register int i = l; i <= mid; ++i)
		tu[++tot] = (node){i, f[i] / (R[i] * A[i] + B[i])};//Attention!!
	sort(tu + 1, tu + 1 + tot);
	stop = 0;//Attention!!
	for (register int nw = 1; nw <= tot; ++nw) {
		int j = tu[nw].cur;
		vectors xl(f[j] / (R[j] * A[j] + B[j]), -f[j] * R[j] / (R[j] * A[j] + B[j]));
		while (stop > 1 && (stk[stop] - stk[stop - 1]) * (xl - stk[stop - 1]) <= 0)	--stop;
		stk[++stop] = xl;
	}
	tot = 0;
	for (register int i = mid + 1; i <= r; ++i)	qu[++tot] = (node){i, B[i] / A[i]};
	sort(qu + 1, qu + 1 + tot);
	int st = 1;
	for (register int nw = 1; nw <= tot; ++nw) {
		int i = qu[nw].cur;
		vectors xl(1, B[i] / A[i]);
		while (stop - st >= 1 && (stk[st + 1] - stk[st]) * xl >= 0) ++st;
		MAX(f[i], A[i] * (xl.y * stk[st].x - stk[st].y));
	}
	cdq(mid + 1, r);
}
...
int main() {
	...
    for (register int i = 1; i <= n; ++i) {
		scanf("%lf%lf%lf", &A[i], &B[i], &R[i]);
	}
	f[1] = s;
	cdq(1, n);//Attention!!
    ...
}
posted @ 2020-08-03 21:49  JiaZP  阅读(93)  评论(0编辑  收藏  举报