题解 CF1760F Quests

大家好,我是 CQ-C2024 蒟蒻 CJH。

(最近一次更新:2022/12/20 21:16,发现 $k$ 无解的情况笔误,已修改,感谢 @budaiyanjing 的提醒)

题意

有 $n$ 个任务,完成第 $i$ 个任务可以获得 $a_i$ 个金币,完成一个任务在未来 $k$ 天内不能再次完成。你要在 $d$ 天内获得 $c$ 个金币,求最大的 $k$。

分析思路

貌似看各位大佬都是用的二分,我这里补一篇贪心排序的题解。

首先将 $a$ 数组从大到小排序,$k$ 此时有三种情况:

一、$k$ 无解

考虑最坏的情况,如果 $k=0$ 时,在 $d$ 天都做利润最高的那个任务,如果仍不能达到 $c$ 个,即 $d \times a_1 < c$,则判定为无解。

二、$k$ 为无穷大

在这 $d$ 天内,将所有的任务只做一遍即可达到 $c$ 个,即 $\sum\limits_{i=1}^{\min\{d,n\}}a_i \ge c$,则 $k$ 为无穷大。

三、$k$ 有解

这里在 $d-1 \sim 0$ 中枚举 $k$,我们将每 $k+1$ 视为一个循环,且每个循环中做最大利润的任务,余下的天数就做利润从大到小的任务。

注意事项

$c\le 10^{16}$,需要用 long long 存储,不开 long long 见祖宗

代码

//the code is from chenjh
#include<bits/stdc++.h>
#define MAXN 200002
using namespace std;
typedef long long LL;
int a[MAXN];
LL b[MAXN];
void solve(){
    int n,d;LL c;
    scanf("%d%lld%d",&n,&c,&d);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1,greater<int>());//从大到小排序。
    if((LL)a[1]*d<c){puts("Impossible");return;}//判断无解。
    for(int i=1;i<=n;i++) b[i]=b[i-1]+a[i];//前缀和数组,表示前 i 个任务的利润。
    if(b[min(n,d)]>=c){puts("Infinity");return;}//判断是否无穷大。
    int k;
    for(k=d-1;k>=0;k--){//枚举 k。
        int x=d%(k+1);//判断余下的天数。
        if(b[min(n,x)]+d/(k+1)*b[min(n,k+1)]>=c){//一定要取最小值,要不然就会访问到 n 个任务以外!
            printf("%d\n",k);
            return;
        }
    }
    puts("Impossible");
}
int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
    return 0;
}
posted @ 2022-11-23 15:31  Chen_Jinhui  阅读(7)  评论(0)    收藏  举报  来源