题目大意:「餐巾计划问题」加强版。$n \leq 5 \times 10^5$
凸函数证明待填坑。
贪心策略为,优先选取购买的玩具,其次从前往后选取慢洗店的玩具,如果都不行,再从后往前选取快洗店的玩具。
之所以快洗店的玩具从后往前选取,是因为前面的慢洗店玩具有可能会在下一天用到,所以要留着前面的,比如下面的样例:
4 1 3 3 1 4
3
1
1
4
最优方案是购买 $4$ 个玩具,第二天送 $1$ 个去快洗店(而非第一天),第一天送 $3$ 个去慢洗店,费用为 $25$。
#include <cstdio>
#include <cstring>
const int N = 5000005, INF = 0x7fffffff;
int q1[N], q2[N], q3[N], h1, h2, h3, t1, t2, t3;
int a[N], b[N], n, d1, d2, c1, c2, tc, ans = INF;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
int min(int x, int y) {
return x < y ? x : y;
}
void swap(int &x, int &y) {
x ^= y, y ^= x, x ^= y;
}
int calc(int k) {
int res = k * tc;
h1 = h2 = h3 = 1, t1 = t2 = t3 = 0;
memcpy(b, a, sizeof a);
for (int i = 1; i <= n; ++i) {
while (h3 <= t3 && q3[h3] + d2 <= i)
q2[++t2] = q3[h3], ++h3;
for (int j = 1; j <= a[i]; ++j) {
if (k) --k;
else {
while (h1 <= t1 && !b[q1[h1]]) ++h1;
if (h1 <= t1 && q1[h1] + d1 <= i) --b[q1[h1]], res += c1;
else {
while (h2 <= t2 && !b[q2[t2]]) --t2;
if (h2 <= t2) --b[q2[t2]], res += c2;
else return INF;
}
}
q1[++t1] = i, q3[++t3] = i;
}
}
ans = min(ans, res);
return res;
}
int main() {
int l = 1, r = 0;
n = read(), d1 = read(), d2 = read(), c1 = read(), c2 = read(), tc = read();
if (c1 > c2) swap(d1, d2), swap(c1, c2);
for (int i = 1; i <= n; ++i) a[i] = read(), r += a[i];
while (l < r) {
int mid = l + (r - l >> 1);
if (calc(mid) < calc(mid + 1)) r = mid;
else l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
时间复杂度 $O(n \log n)$