Loading

Educational Codeforces Round 114 (Rated for Div. 2) C. Slay the Dragon


题目链接

题目大意

给你\(n\)个人,每个人有\(a_i\)点攻击力,你需要选\(1\)个人去击杀敌人,剩下的人守卫。当选中的人的攻击力大于等于敌人的防御力\(x\)且剩下的人攻击力之和大于等于敌人的攻击力\(y\),则算成功。你可以\(1\)枚金币提高任何一个人的攻击力,问你最小的花费是多少。

题目分析

给定一个敌人\((防御力,攻击力)=(x,y)\),令派出的人的攻击力是\(k\),所有人的攻击力之和为\(sum\),那么花费的金币数为: \(\max(0, x - k) + \max(0, y - (sum - k))\)。之后对每个人的能力进行枚举,显然这么做会超时。

先对\(n\)个人的攻击力进行排序,进行去重操作,因为存在攻击力相同的情况,没有必要重复判断。

之后,二分找到第一个大于等于\(x\)的元素,那么之后的元素就没有必要枚举,因为金币的花费会越来越多。之后我们从找到的元素再往前枚举,此时\(\max(0, x - k)\)会逐渐增大,\(会逐渐\max(0, y - (sum - k))\)减小,总的趋势是金币数先小后大,所以我们只需要向前枚举,找到金币的极小点即可。

AC代码

#include <bits/stdc++.h>
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;

constexpr int N = 2e5 + 100;
typedef long long LL;
int n, m;
LL a[N], sum;

inline LL cost(LL x, LL y)
{
    LL res = LONG_LONG_MAX;
    int k = lower_bound(a, a + n, x) - a;
    if (k == n) k --;
    for (int i = k; i; i--)
    {
        LL t = max(0LL, x - a[i]) + max(0LL, y - sum + a[i]);
        if (res > t) res = t;
        else break;
    }
    return res;
}


int main()
{
    // 神奇,本题比用scanf快好几倍
    io;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], sum += a[i];
    sort(a + 1, a + n + 1);
    n = unique(a + 1, a + n + 1) - a;

    cin >> m;
    while (m --)
    {
        LL x, y; cin >> x >> y;
        cout << cost(x, y) << '\n';
    }

    return 0;
}

posted @ 2021-09-21 10:08  Frank_Ou  阅读(75)  评论(0编辑  收藏  举报