C. Messenger in MAC

C. Messenger in MAC

In the new messenger for the students of the Master's Assistance Center, Keftemerum, an update is planned, in which developers want to optimize the set of messages shown to the user. There are a total of $n$ messages. Each message is characterized by two integers $a_i$ and $b_i$. The time spent reading the set of messages with numbers $p_1, p_2, \ldots, p_k$ ($1 \le p_i \le n$, all $p_i$ are distinct) is calculated by the formula:

$$\Large \sum_{i=1}^{k} a_{p_i} + \sum_{i=1}^{k - 1} |b_{p_i} - b_{p_{i+1}}|$$

Note that the time to read a set of messages consisting of one message with number $p_1$ is equal to $a_{p_1}$. Also, the time to read an empty set of messages is considered to be $0$.

The user can determine the time $l$ that he is willing to spend in the messenger. The messenger must inform the user of the maximum possible size of the set of messages, the reading time of which does not exceed $l$. Note that the maximum size of the set of messages can be equal to $0$.

The developers of the popular messenger failed to implement this function, so they asked you to solve this problem.

Input

Each test consists of multiple test cases. The first line contains a single integer $t$ ($1 \leq t \leq 5 \cdot 10^4$) — the number of test cases. The description of the test cases follows.

The first line of each test case contains two integers $n$ and $l$ ($1 \leq n \leq 2000$, $1 \leq l \leq 10^9$) — the number of messages and the time the user is willing to spend in the messenger.

The $i$-th of the next $n$ lines contains two integers $a_i$ and $b_i$ ($1 \le a_i, b_i \le 10^9$) — characteristics of the $i$-th message.

It is guaranteed that the sum of $n^2$ over all test cases does not exceed $4 \cdot 10^6$.

Output

For each test case, output a single integer — the maximum possible size of a set of messages, the reading time of which does not exceed $l$.

Example

input

5
5 8
4 3
1 5
2 4
4 3
2 3
1 6
4 10
3 12
4 8
2 1
2 12
5 26
24 7
8 28
30 22
3 8
17 17
5 14
15 3
1000000000 998244353
179 239
228 1337
993 1007

output

3
1
2
1
0

Note

In the first test case, you can take a set of three messages with numbers $p_1 = 3$, $p_2 = 2$, and $p_3 = 5$. The time spent reading this set is equal to $a_3 + a_2 + a_5 + |b_3 - b_2| + |b_2 - b_5| = 2 + 1 + 2 + |4 - 5| + |5 - 3| = 8$.

In the second test case, you can take a set of one message with number $p_1 = 1$. The time spent reading this set is equal to $a_1 = 4$.

In the fifth test case, it can be shown that there is no such non-empty set of messages, the reading time of which does not exceed $l$.

 

解题思路

  赛时是用 dp 做的,看了下官方题解貌似还可以暴力贪心。

  首先对于任意一组选出的方案 $p_1, \ldots, p_k$,当 $b_{p_1} \leq \ldots \leq b_{p_k}$ 时,$\sum\limits_{i=1}^{k - 1} |b_{p_i} - b_{p_{i+1}}|$ 的部分一定有最小代价 $b_{p_k} - b_{p_1}$。

  这里给出简单证明,当 $b$ 有序时,$|b_{i} - b_{i-1}| + |b_{i+1} - b_i| = b_{i+1} - b_{i-1}$。如果 $b$ 不是有序,那么一定存在两个相邻元素前者比后者大,交换 $b_i$ 和 $b_{i+1}$,此时 $|b_{i+1} - b_{i-1}| + |b_i - b_{i+1}| = 2 \cdot b_{i+1} - b_{i-1} - b_i = b_{i+1} - b_{i-1} + b_{i+1} - b_{i} > b_{i+1} - b_{i-1}$。因此当相邻元素都是有序时,代价最小。

  假设现在数对 $(a_i, b_i)$ 是按照 $b_i$ 从小到大排序的。定义 $f(i,j)$ 表示从前 $j$ 个数对中恰好选择了 $i$ 个,且一定选择第 $j$ 个数对的方案中代价最小值。根据上一个选择的数对进行状态划分,转移方程就是 $f(i,j) = \min\limits_{1 \leq k < j}\{ f(i-1,k) - b_k \} + a_j + b_j$。

  显然直接暴力转移的话时间复杂度是 $O(n^3)$。可以发现对于固定 $i$,每次转移都是求前缀 $k \in [1 \sim j-1]$ 中 $f(i-1,k) - b_k$ 的最小值,因此在枚举到 $i$ 时依次遍历 $j$,并用 $g(j)$ 来维护前缀 $j$ 中 $f(i-1,k) - b_k$ 的最小值。这样转移方程就变成了 $f(i,j) = g(j-1) + a_j + b_j$。

  最后暴力枚举 $f(i,j)$ 的所有状态,如果 $f(i,j) \leq l$ 那么将答案与 $i$ 取最大值。

  AC 代码如下,时间复杂度为 $O(n^2)$:

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

typedef long long LL;

const int N = 2010;
const LL INF = 0x3f3f3f3f3f3f3f3f;

int a[N], b[N], p[N];
LL f[N][N], g[N];

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", a + i, b + i);
        p[i] = i;
    }
    sort(p + 1, p + n + 1, [&](int i, int j) {
        return b[i] < b[j];
    });
    for (int i = 1; i <= n; i++) {
        f[0][i] = f[i][0] = INF;
    }
    for (int i = 1; i <= n; i++) {
        g[0] = f[i - 1][0];
        for (int j = 1; j <= n; j++) {
            g[j] = min(g[j - 1], f[i - 1][j] - b[p[j]]);
        }
        for (int j = 1; j <= n; j++) {
            f[i][j] = g[j - 1] + a[p[j]];
            if (i > 1) f[i][j] += b[p[j]];
        }
    }
    int ret = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (f[i][j] <= m) ret = i;
        }
    }
    printf("%d\n", ret);
}

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

  再给出贪心的做法。由于数对 $(a_i, b_i)$ 已经按照 $b_i$ 从小到大排序,当选择的方案中第一个数对是 $(a_l, b_l)$,最后一个数对是 $(a_r, b_r)$,那么 $\sum\limits_{i=1}^{k - 1} |b_{p_i} - b_{p_{i+1}}|$ 部分的代价是固定的 $b_r - b_l$。现在要在 $[l,r]$ 中满足 $\sum{a_i} + b_r - b_l \leq l$ 的条件下选择尽可能多的数对。显然贪心的做法是按照 $a_i$ 从小到大去选,直到不满足条件。

  所以具体做法是暴力枚举 $l$ 和 $r$,在固定 $l$ 枚举 $r$ 时开一个 std::priority_queue 存储 $[l,r]$ 中的 $a_i$,当 $\sum{a_i} + b_r - b_l \leq l$ 不满足时把最大的元素从中删除。最后如果满足条件此时优先队列的大小就是最多能选择的数对,将其与答案取最大值。

  然后有疑问的地方是,如果此时优先队列中不同时存在 $a_l$ 和 $a_r$,那么这么做是否还是正确的?此时优先队列中的 $a_i$ 都来自 $(l,r)$ 中的数对,意味着此时的代价 $\sum{a_k} + b_r - b_l$ 一定不小于 $\sum{a_k} + b_i - b_j, \quad (l < i \leq j < r)$,因此对答案没有影响。

  AC 代码如下,时间复杂度为 $O(n^2\log{n})$:

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

typedef long long LL;

const int N = 2010;

int a[N], b[N], p[N];

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", a + i, b + i);
        p[i] = i;
    }
    sort(p + 1, p + n + 1, [&](int i, int j) {
        return b[i] < b[j];
    });
    int ret = 0;
    for (int i = 1; i <= n; i++) {
        LL s = 0;
        priority_queue<int> pq;
        for (int j = i; j <= n; j++) {
            s += a[p[j]];
            pq.push(a[p[j]]);
            while (!pq.empty() && s + b[p[j]] - b[p[i]] > m) {
                s -= pq.top();
                pq.pop();
            }
            if (s + b[p[j]] - b[p[i]] <= m) ret = max(ret, int(pq.size()));
        }
    }
    printf("%d\n", ret);
}

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

 

参考资料

  Codeforces Round #932 (Div. 2) Editorial:https://codeforces.com/blog/entry/126662

posted @ 2024-03-06 16:32  onlyblues  阅读(60)  评论(6编辑  收藏  举报
Web Analytics