跳转至

E - Plants vs. Zombies

基本信息

题目出处2018 ICPC 亚洲区域赛青岛站
队伍通过率121/373 (32.4%)

题解

容易想到二分答案,接下来考虑如何检验答案 \(x\)

由于每一次浇水前必须先移动,因此为了把第一棵植物浇到高度 \(x\) 同时又不浪费水,就需要在第一和第二棵植物之间反复移动。完成第一棵植物的浇水后,为了把第二棵植物浇到高度 \(x\) 同时又不浪费水,就需要在第二和第三棵植物之间反复移动...

模拟以上贪心过程即可。时间复杂度 \(\mathcal{O}(n\log (m \times \max a_i))\)

参考代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
#define MAXN ((int) 1e5)
using namespace std;

int n, A[MAXN + 10];
long long m;

// 表示目前每棵植物的高度
long long B[MAXN + 10];

bool check(long long LIM) {
    long long step = 0;
    memset(B, 0, sizeof(long long) * (n + 3));
    for (int i = 1; i <= n; i++) {
        if (LIM > B[i]) {
            // 第 i 棵植物需要继续浇水
            // 在它和第 (i + 1) 棵植物之间反复移动
            long long t = LIM - B[i];
            t = (t + A[i] - 1) / A[i];
            step += t * 2 - 1;
            // 步数超出限制就即刻退出
            // 否则数据范围可能超出 long long
            if (step > m) return false;
            B[i + 1] += A[i + 1] * (t - 1);
        } else {
            // 第 i 棵植物不需要继续浇水
            // 直接路过,但步数还要算
            // 这里步数可能会超过限制
            // 但是只要右边没有其它需要浇水的植物,就不会返回 false
            step++;
        }
    }
    return true;
}

void solve() {
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &A[i]);

    // 二分答案
    long long head = 0, tail = 1e18;
    while (head < tail) {
        long long mid = (head + tail + 1) >> 1;
        if (check(mid)) head = mid;
        else tail = mid - 1;
    }
    printf("%lld\n", head);
}

int main() {
    int tcase; scanf("%d", &tcase);
    while (tcase--) solve();
    return 0;
}