欢迎光临
我们一直在努力

Codeforces Round 835 (Div. 4) — F. Quests

Codeforces Round 835 (Div. 4) — F. Quests

训练思维的算法题,值得一做,题目直接展示了,后面会有题解,不要有畏难心理,纯手搓思路!

F. Quests

time limit per test 3 seconds

memory limit per test 256 megabytes

There are n quests. If you complete the i-th quest, you will gain ai coins. You can only complete at most one quest per day. However, once you complete a quest, you cannot do the same quest again for k days. (For example, if k=2 and you do quest 1 on day 1, then you cannot do it on day 2 or 3, but you can do it again on day 4.)

You are given two integers c and d. Find the maximum value of k such that you can gain at least c coins over d days. If no such k exists, output Impossible. If k can be arbitrarily large, output Infinity.


Input

The input consists of multiple test cases. The first line contains an integer t (1≤t≤10^4) — the number of test cases. The description of the test cases follows.

The first line of each test case contains three integers n,c,d (2≤n≤2⋅10^5; 1≤c≤10^16; 1≤d≤2⋅10^5) — the number of quests, the number of coins you need, and the number of days.

The second line of each test case contains n integers a1,a2,…,an (1≤ai≤10^9) — the rewards for the quests.

The sum of n over all test cases does not exceed 2⋅10^5, and the sum of d over all test cases does not exceed 2⋅10^5.


Output

For each test case, output one of the following.

If no such k exists, output Impossible.

If k can be arbitrarily large, output Infinity.

Otherwise, output a single integer — the maximum value of k such that you can gain at least c coins over d days.

Please note, the checker is case-sensitive, and you should output strings exactly as they are given.


Example

InputCopy

6
2 5 4
1 2
2 20 10
100 10
3 100 3
7 2 6
4 20 3
4 5 6 7
4 100000000000 2022
8217734 927368 26389746 627896974
2 20 4
5 1

OutputCopy

2
Infinity
Impossible
1
12
0


Note

In the first test case, one way to earn 5 coins over 4 days with k=2 is as follows:

Day 1: do quest 2, and earn 2 coins.

Day 2: do quest 1, and earn 1 coin.

Day 3: do nothing.

Day 4: do quest 2, and earn 2 coins.

In total, we earned 2+1+2=5 coins.

In the second test case, we can make over 20 coins on the first day itself by doing the first quest to earn 100 coins, so the value of k can be arbitrarily large, since we never need to do another quest.

In the third test case, no matter what we do, we can’t earn 100 coins over 3 days.


题解

核心思路:排序 + 贪心判断 + 二分答案。

固定一个冷却时间后,可以贪心求出最多能拿多少金币;由于冷却时间越大,限制越强,能拿到的金币不会增加,因此可以二分最大可行值。


一、题意转化

题目要求找到最大的 k,使得在 d 天内至少获得 c 枚金币。

其中 k 的含义是:如果今天做了某个任务,那么接下来的 k 天都不能再做同一个任务。

例如 k = 2:

天数状态
第 1 天 做任务 A
第 2 天 不能做任务 A
第 3 天 不能做任务 A
第 4 天 可以再次做任务 A

所以,同一个任务的重复周期并不是 k,而是:

m = k + 1

代码里更方便二分的是周期 m,最后答案再转回:

k = m – 1


二、固定周期 m 后怎么求最大收益

先把所有任务奖励从大到小排序。

排序后:

a[0] ≥ a[1] ≥ a[2] ≥ …

也就是说:

下标含义
a[0] 奖励最高的任务
a[1] 奖励第二高的任务
a[2] 奖励第三高的任务

固定周期 m 后,最优策略就是按奖励从高到低循环做任务。

如果 m = 3,任务下标会这样循环:

0, 1, 2, 0, 1, 2, 0, 1, 2, …

这个循环正好可以用:

int idx = i % m;

表示。

其中 i 表示第几天,从 0 开始。

例如 m = 3:

ii % 3
0 0
1 1
2 2
3 0
4 1
5 2

所以第 i 天尝试做的任务就是:

a[i % m]


三、为什么要判断 idx < n

如果周期 m 比任务数量 n 大,那么有些周期位置没有任务可以做。

例如:

n = 2, m = 4

任务只有:

a[0], a[1]

但是 i % m 会循环出:

0, 1, 2, 3, 0, 1, 2, 3, …

当 idx = 2 或 idx = 3 时,不存在对应任务,所以当天只能休息。

因此判断收益时需要写:

if (idx < n) {
res += a[idx];
}


四、为什么可以二分

周期 m 越大,说明同一个任务要等更久才能重复做。

所以:

周期限制收益趋势
m 小 冷却短,任务重复快 更容易达到目标
m 大 冷却长,任务重复慢 更难达到目标

因此可行性具有单调性:

如果某个 m 可行,那么更小的 m 一定可行。

如果某个 m 不可行,那么更大的 m 一定不可行。

所以可以用二分查找最大的可行周期 m。

二分模板:

while (l < r) {
int mid = (l + r + 1) >> 1;

if (check(mid)) {
l = mid;
} else {
r = mid 1;
}
}

这里要找最大可行值,所以使用右中位数:

int mid = (l + r + 1) >> 1;


五、边界解释

1. 为什么 l = 0

周期 m 的最小合法值是 1。

因为:

m = k + 1

而 k 的最小值是 0,所以 m 最小是 1。

这里把 l 初始化为 0:

int l = 0;

0 不是合法周期,而是一个标记。

它表示当前还没有找到任何可行的周期。

如果最后 l == 0,说明连 m = 1 都不可行。

m = 1 对应 k = 0,也就是每天都可以做收益最高的任务。

如果这样都不能达到 c,答案就是:

Impossible


2. 为什么 r = d + 1

总共只有 d 天。

如果:

m > d

那么在这 d 天里,同一个任务不会被重复做。

例如 d = 4, m = 5,前四天的下标是:

0, 1, 2, 3

还没循环回来,天数已经结束。

所以 m = d + 1 可以用来代表“不需要重复任务”的情况。

如果 m = d + 1 仍然可以达到 c,说明根本不用重复任务也能完成目标。

那么 k 可以任意大,答案就是:

Infinity


六、算法流程

  • 读入 n, c, d 和数组 a。

  • 将 a 从大到小排序。

  • 二分周期 m = k + 1。

  • 对每个 mid,模拟 d 天:

    • 第 i 天的位置是 i % mid;
    • 如果 idx < n,就加上 a[idx];
    • 如果 idx >= n,当天休息。
  • 如果总收益 res >= c,说明当前周期可行,尝试更大的周期。

  • 如果总收益 res < c,说明当前周期不可行,缩小周期。

  • 二分结束后:

    • 若 l == d + 1,输出 Infinity;
    • 若 l == 0,输出 Impossible;
    • 否则输出 l – 1。

  • 七、正确性证明

    引理 1:固定周期时,优先选择高奖励任务最优

    对于固定的周期 m,每天能做的任务位置是确定循环的。

    由于奖励越高越应该尽早完成,并且越早完成也越早进入冷却,之后越早可以再次完成,所以把任务奖励从大到小排序,并按顺序循环选择,不会比其他选择更差。

    因此固定周期时,该贪心策略可以得到最大收益。


    引理 2:可行性具有单调性

    当周期 m 增大时,同一个任务重复所需的间隔变长。

    因此高奖励任务出现的频率不会增加,总收益不会增加。

    所以:

    • 如果某个 m 可行,则更小的 m 一定可行;
    • 如果某个 m 不可行,则更大的 m 一定不可行。

    因此可以二分最大的可行周期。


    引理 3:特殊情况判断正确

    如果 l == 0,说明连最小周期 m = 1 都不可行。

    m = 1 对应 k = 0,是限制最宽松的情况。

    如果这种情况都不能达到目标,则不存在任何合法 k,输出 Impossible 正确。

    如果 l == d + 1,说明周期大于总天数时仍然可行。

    这表示在 d 天内完全不需要重复任务也能达到目标,因此 k 可以任意大,输出 Infinity 正确。

    综上,算法正确。


    八、易错点总结

    本题最容易错的是变量含义和二分边界。

    1. 不要混淆 k 和 m

    题目要求输出的是 k,但代码二分的是:

    m = k + 1

    所以最后输出:

    cout << l 1 << '\\n';


    2. 不要把输出判断写进 while 循环

    错误结构:

    while (l < r) {
    ...
    if (l == d + 1) {
    cout << "Infinity\\n";
    return;
    }
    }

    正确结构:

    while (l < r) {
    ...
    }

    if (l == d + 1) {
    cout << "Infinity\\n";
    } else if (l == 0) {
    cout << "Impossible\\n";
    } else {
    cout << l 1 << '\\n';
    }


    3. 二分最大值时要取右中位数

    应写成:

    int mid = (l + r + 1) >> 1;

    如果写成:

    int mid = (l + r) >> 1;

    当 l 和 r 相邻时,可能死循环。


    4. 关于数组写法

    ll a[n] 在 Codeforces 的 GNU C++ 中通常可以通过,但不是标准 C++。

    更稳的写法是全局数组:

    const int N = 200010;
    ll a[N];

    或者使用 vector:

    vector<ll> a(n);


    九、完整代码

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

    typedef long long ll;

    const int N = 200010;
    ll a[N];

    void solve() {
    int n, d;
    ll c;

    cin >> n >> c >> d;

    for (int i = 0; i < n; i++) {
    cin >> a[i];
    }

    sort(a, a + n, greater<ll>());

    /*
    题目中的 k 是冷却天数。
    代码中二分的是周期 m = k + 1。
    最后答案需要输出 k = m – 1。
    */

    int l = 0, r = d + 1;

    while (l < r) {
    int mid = (l + r + 1) >> 1;

    ll res = 0;

    for (int i = 0; i < d; i++) {
    int idx = i % mid;

    if (idx < n) {
    res += a[idx];
    }

    if (res >= c) {
    break;
    }
    }

    if (res >= c) {
    l = mid;
    } else {
    r = mid 1;
    }
    }

    if (l == d + 1) {
    cout << "Infinity\\n";
    return;
    }

    if (l == 0) {
    cout << "Impossible\\n";
    return;
    }

    cout << l 1 << '\\n';
    }

    int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;

    while (t) {
    solve();
    }

    return 0;
    }


    十、复杂度分析

    排序复杂度为 O(n log n)。

    二分周期 m 的次数为 O(log d)。

    每次判断最多模拟 d 天,所以判断部分复杂度为 O(d log d)。

    因此总时间复杂度为:

    O(n log n + d log d)

    空间复杂度为:

    O(n)


    十一、总结

    本题关键是理解:

    周期 m = k + 1

    固定周期后,按奖励从高到低循环选择任务即可得到最大收益。

    由于周期越大,限制越强,收益不会增加,因此可以二分最大的可行周期。

    最终答案为:

    k = m – 1

    核心: 排序 + 贪心模拟 + 二分答案。

    赞(0)
    未经允许不得转载:171主机测评 » Codeforces Round 835 (Div. 4) — F. Quests
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址