[USACO22OPEN] Apple Catching G

发布时间 2023-11-10 18:20:06作者: onlyblues

[USACO22OPEN] Apple Catching G

题目描述

天上下苹果了!在某些时刻,一定数量的苹果会落到数轴上。在某些时刻,Farmer John 的一些奶牛将到达数轴并开始接苹果。

如果一个苹果在没有奶牛接住的情况下落到数轴上,它就会永远消失。如果一头奶牛和一个苹果同时到达,奶牛就会接住苹果。每头奶牛每秒可以移动一单位距离。一旦一头奶牛接住了一个苹果,她就会离开数轴。

如果 FJ 的奶牛以最优方式合作,她们总共能接住多少个苹果?

输入格式

输入的第一行包含 $N$($1\le N\le 2\cdot 10^5$),为苹果落到数轴上的次数或 FJ 的奶牛出现的次数。

以下 $N$ 行每行包含四个整数 $q_i$,$t_i$,$x_i$ 和 $n_i$($q_i\in \{1,2\}, 0\le t_i\le 10^9, 0\le x_i\le 10^9, 1\le n_i\le 10^3$)。

  • 如果 $q_i=1$,意味着 FJ 的 $n_i$ 头奶牛在 $t_i$ 时刻来到数轴上的 $x_i$ 位置。
  • 如果 $q_i=2$,意味着 $n_i$ 个苹果在 $t_i$ 时刻落到了数轴上的 $x_i$ 位置。

输入保证所有有序对 $(t_i,x_i)$ 各不相同。

输出格式

输出 FJ 的奶牛总计能接住的苹果的最大数量。

样例 #1

样例输入 #1

5
2 5 10 100
2 6 0 3
2 8 10 7
1 2 4 5
1 4 7 6

样例输出 #1

10

样例 #2

样例输入 #2

5
2 5 10 100
2 6 0 3
2 8 11 7
1 2 4 5
1 4 7 6

样例输出 #2

9

提示

【样例解释 1】

在这个例子中,在 $t=5$ 时刻落地的 $100$ 个苹果均不能被接住。以下是一种接住 $10$ 个苹果的方式:

  • FJ 的所有六头 $t=4$ 时刻到达的奶牛各接一个 $t=8$ 时刻落地的苹果。
  • FJ 的一头 $t=2$ 时刻到达的奶牛接一个 $t=8$ 时刻落地的苹果。
  • 余下三头 $t=2$ 时刻到达的奶牛各接一个 $t=6$ 时刻落地的苹果。

【样例解释 2】

再一次地,在 $t=5$ 时刻落地的苹果均不能被接住。除此之外,在 $t=2$ 时刻到达的奶牛均不能接住 $t=8$ 时刻落地的苹果。以下是一种接住 $9$ 个苹果的方式:

  • FJ 的所有六头 $t=4$ 时刻到达的奶牛各接一个 $t=8$ 时刻落地的苹果。
  • 余下三头 $t=2$ 时刻到达的奶牛各接一个 $t=6$ 时刻落地的苹果。

 

解题思路

  考虑 $t_i$ 时刻 $x_i$ 位置上的奶牛可以接到哪些苹果,很明显这些苹果的 $t_j$ 和 $x_j$ 要满足 $x_j \in [ x_i - (t_j - t_i), \, x_i + (t_j - t_i) ]$,即

$$\begin{cases}
x_j \geq x_i - t_j + t_i \\
x_j \leq x_i + t_j - t_i
\end{cases}
\longrightarrow 
\begin{cases}
x_j + t_j \geq x_i + t_i \\
x_j - t_j \leq x_i - t_i
\end{cases}$$

  推到这里的时候卡住了,要同时满足两个条件不知道要怎么实现。其实可以先按照 $x_i - t_i$ 这个关键字从小到大排序,这样就把上式第二个条件固定了。其中在排序时对于 $x_i - t_i$ 相等的情况,应该优先让苹果排在前面,奶牛排在后面,因为现在是看奶牛能选哪些苹果。接下来只需要考虑第一个式子。

  从左到右依次枚举,用一个 $\text{std::multiset}$ 来维护前缀中剩余的苹果,当枚举到苹果时直接把 $x_i + t_i$ 和苹果数量压入集合中。如果枚举到奶牛 $i$,那么集合中满足 $x_j + t_j \geq x_i + t_i$ 的苹果都可以选。从贪心的角度,我们应该按 $x_j + t_j$ 从依次小到大来选择,这是因为 $x_j + t_j$ 越小,那么能接住这些苹果的奶牛越少,现在奶牛 $i$ 能接到应该优先选择。

  时间复杂度的计算,外层循环枚举 $n$ 个询问,当枚举到奶牛时又要内层循环枚举集合中的元素,看上去好像是 $O(n^2 \log{n})$ 级别的时间复杂度,实际上内层循环中每次不是把集合中的一个元素删掉(把某个时刻位置的全部苹果选完),就是把这群牛给删掉(接住的苹果达到上限),因此整个过程中内层循环执行次数是 $O(n)$ 级别的,因此时间复杂度应该是 $O(n \log{n})$。

  AC 代码如下:

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;

const int N = 2e5 + 10;

struct Node {
    int op, t, x, s;
    
    bool operator<(Node &a) {
        if (x - t != a.x - a.t) return x - t < a.x - a.t;
        return op > a.op;
    }
}q[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d %d %d", &q[i].op, &q[i].t, &q[i].x, &q[i].s);
    }
    sort(q, q + n);
    multiset<PII> st;
    int ret = 0;
    for (int i = 0; i < n; i++) {
        if (q[i].op == 2) {
            st.insert({q[i].x + q[i].t, q[i].s});
        }
        else {
            auto it = st.lower_bound({q[i].x + q[i].t, -1});
            while (it != st.end()) {
                if (q[i].s <= it->second) {
                    ret += q[i].s;
                    if (q[i].s != it->second) st.insert({it->first, it->second - q[i].s});
                    st.erase(it);
                    break;
                }
                else {
                    ret += it->second;
                    q[i].s -= it->second;
                    it = st.erase(it);
                }
            }
        }
    }
    printf("%d", ret);
    
    return 0;
}

 

参考资料

   【题解】P8272 [USACO22OPEN] Apple Catching G:https://www.luogu.com.cn/blog/EricQian/solution-p8272