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!!
...
}