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;
}