Post

CF1873F Money Trees

CF1873F Money Trees

这篇博客记录了解决题目 CF1873F Money Trees 的思路和代码,其中使用双指针和二分答案的方法来找到满足条件的最长线段长度。

题目大意

有 $n$ 棵树,每棵树高 $h_i$,有 $a_i$ 个果子。

你可以选一对 $l$ 和 $r$,使得 $h_{i+1}\mid h_i\space(l\le i<r)$ 且 $a_l+a_{l+1}+\dots +a_r\le k$。

求 $r-l+1$ 的最大值。

思路

先 $O(N)$ 的找出所有符合整除条件的长线段,然后在每个长线段中找一条最长的和不超过 $k$ 的线段。这一操作可以用二分答案或双指针来实现。最开始使用贪心,贪心地去删除左右两边一个更大的值,但是这样贪心是错误的。

注意到对于每个左端点 $l$,它的最大 $r$ 是不降的,符合双指针的使用场景,因此也可以直接在遍历所有树的时候用双指针处理。

代码

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
int n, k, a[maxN + 5], h[maxN + 5], res, sum[maxN + 5];
int check(int l, int r, int len) {
    int cnt = (r - l + 1) - len + 1;
    rep(i, 0, cnt - 1) {
        int st = l + i, ed = l + i + len - 1;
        if (sum[ed] - sum[st - 1] <= k)
            return 1;
    }
    return 0;
}
int calc(int l, int r) { //[l,r)
    int sum = 0;
    r--;
    int left = 0, right = r - l + 1, res = 0;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (check(l, r, mid)) {
            res = mid, left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return res;
}
void solve() {
    res = 0;
    cin >> n >> k;
    rep(i, 1, n) cin >> a[i];
    rep(i, 1, n) cin >> h[i];
    rep(i, 1, n) sum[i] = sum[i - 1] + a[i];
    int l = 1, r;
    for (r = 2; r <= n; r++) {
        if (h[r - 1] % h[r] != 0) {
            res = max(res, calc(l, r));
            l = r;
        }
    }
    res = max(res, calc(l, r));
    cout << res << endl;
}
This post is licensed under CC BY 4.0 by the author.