[蓝桥杯2022初赛C组] 技能升级(二分答案*)
思路:
-
比较特别的二分答案题。通常的思路是题目要求什么就二分答案什么。但此题二分 最多能提高多少攻击力 是不可行的,因为根据这个 “答案” 你没法确定每个技能升级多少次,跟 M 无法联系,也就不存在 check 关系。 正解是二分 最后一次升级技能提升了多少攻击力。 我们升级技能的朴素过程,肯定是贪心的思路,找到所有技能中攻击力最高的,升级它,再改变它的点数。可以发现,当我们知道了最后一次升级技能提升了多少攻击力,由于之前升级技能的贪心过程,之前升级技能提升的攻击力都不少于 该次,而升级技能点数减少的过程是一个等差数列,我们显然能 O ( 1 ) O(1) O(1) 的时间知道每个技能升级了几次。最后根据升级技能的次数和 M 对比,check 关系就出来了。 知道了 每个技能升级的次数,显然也可以推导出答案最多可以提高多少点攻击力。 还有些细节问题:check 时,累计的是升级技能攻击力 大于 mid 的次数。算答案时,先累计优先升级比最后一次攻击力高的,再累计相同的。
C o d e : Code: Code:
#include<bits/stdc++.h> #include<unordered_map> #include<unordered_set> #define mem(a,b) memset(a,b,sizeof a) #define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)) #define debug(x) cout<<"target is "<<x<<endl #define forr(a,b,c) for(int a=b;a<=c;a++) #define all(a) a.begin(),a.end() #define oper(a) operator<(const a& ee)const #define endl " " #define ul (u << 1) #define ur (u << 1 | 1) using namespace std; typedef long long ll; typedef pair<int, int> PII; const int N = 100010, M = 200010, MM = 110; int INF = 0x3f3f3f3f, mod = 1e9 + 7; ll LNF = 0x3f3f3f3f3f3f3f3f; int n, m, k; ll a[N], b[N]; bool check(int mid) { ll sum = 0;//统计次数 for (int i = 1; i <= n; i++) { if (a[i] <= mid)continue; //不统计攻击力等于 mid 的升级 sum += (a[i] - mid - 1) / b[i] + 1; } return sum > m; //大于 m 时是不可取的,小于等于逼近 } ll cot(ll be, ll ed, ll xi) { return (be + ed) * xi / 2; } ll cal(int mi) { //计算答案 int summi = 0;//统计攻击力等于 mid 的升级的次数 ll sum = 0; for (int i = 1; i <= n; i++) { if (a[i] < mi)continue; if ((a[i] - mi) % b[i] == 0)summi++; if (a[i] == mi)continue; //贪心,一开始 sum 先加攻击力大于 mid 的升级 //首项末项项数 ll xi = (a[i] - mi - 1) / b[i] + 1; ll ed = a[i] - (xi - 1) * b[i]; sum += cot(a[i], ed, xi); m -= xi; } //如果还有多余的升级次数,再升级 攻击力等于 mid 的技能 while (m && summi) { m--; summi--; sum += mi; } return sum; } void solve() { cin >> n >> m; forr(i, 1, n) { cin >> a[i] >> b[i]; } //二分 最后一次升级技能提升了多少攻击力 int l = 1, r = 1e6 + 10; while (l < r) { int mid = l + r >> 1; if (check(mid))l = mid + 1; else r = mid; } cout << cal(l); } int main() { cinios; int T = 1; forr(t, 1, T) { solve(); } return 0; }