题目链接:https://codeforces.com/contest/1334
A. Level Statistics
题意
一个关卡有玩家的尝试次数和通关次数,按时间顺序给出一个玩家 $n$ 个时刻的数据,判断这些数据是否合理。
思路
- 通关次数不会多于尝试次数:$c_i≤p_i$
- 后一时刻的尝试次数不会多于前一时刻:$p_i≤p_{i-1}$
- 后一时刻的通关次数不会多于前一时刻:$c_i≤c_{i-1}$
- 增加的通关次数不会多于增加的尝试次数:$c_i-c_{i-1}≤p_i-p_{i-1}$
代码
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; int p[n], c[n]; for (int i = 0; i < n; i++) { cin >> p[i] >> c[i]; } for (int i = 0; i < n; i++) { if (c[i] > p[i]) { cout << "NO" << " "; return; } } for (int i = 1; i < n; i++) { if (p[i] < p[i - 1] || c[i] < c[i - 1] || c[i] - c[i - 1] > p[i] - p[i - 1]) { cout << "NO" << " "; return; } } cout << "YES" << " "; } int main() { int t; cin >> t; while (t--) solve(); }
B. Middle Class
题意
已知 $n$ 个人财富值,可以不限次地选取一些人将他们的财富值汇总后平均分配,问最后最多有多少人财富值不少于 $x$ 。
思路
降序排列,分配多余的财富值,第一次不能凑齐 $x$ 时之前的人数即为答案。
代码
#include <bits/stdc++.h> using ll = long long; using namespace std; void solve() { int n, x; cin >> n >> x; int a[n]; for (int & i : a) cin >> i; sort(a, a + n, greater<int>()); ll extra = 0, ans = 0; for (int i : a) { if (i >= x) { extra += i - x; ++ans; } else if (extra >= x - i) { extra -= x - i; ++ans; } } cout << ans << " "; } int main() { int t; cin >> t; while (t--) solve(); }
C. Circle of Monsters
题意
有一圈怪兽,每个怪兽有 $a_i$ 点生命值,每射击 $1$ 次怪兽会掉 $1$ 点生命值,当怪兽死亡时会对下一个怪兽造成 $b_i$ 点伤害,要想消灭所有怪兽最少需要射击多少次。
思路
先求出至少需要射击多少次,即 $sum_{i=0}^{n-1}a[i] - b[i-1]$,然后枚举以哪只怪兽为起点,取总和的最小值即可。
代码
#include <bits/stdc++.h> #define pre(i) ((i - 1 + n) % n) using ll = long long; using namespace std; void solve() { int n; cin >> n; ll a[n], b[n]; for (int i =0 ; i < n; i++) { cin >> a[i] >> b[i]; } ll sum = 0; for (int i = 0; i < n; i++) { sum += max(0LL, a[i] - b[pre(i)]); } ll ans = 1e18; for (int i = 0; i < n; i++) { if (a[i] > b[i]) { ans = min(ans, sum + b[i]); } else { ans = min(ans, sum + a[i]); } } cout << ans << " "; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); }
D. Minimum Euler Cycle
题意
求一个 $n$ 点完全有向图字典序最小的欧拉回路的一段区间。
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
思路
因为是求字典序最小的回路,所以我们每次可以通过折返先走完较小的点,比如 $4$ 个点时:
每次以 1 为起点:1 2 1 3 1 4
每次以 2 为起点:2 3 2 4
每次以 3 为起点:3 4
回到 1 构成回路:1
- 观察发现前 $n-1$ 个区间长度为等差数列,即 $len_i=2*(n-i)$,
- 所以我们可以用前缀和 $pre\_sum$ 记录到每个区间时的元素总个数,之后可以据此用 $lower\_bound$ 二分查找第 $i$ 个元素所在的区间编号 $seg\_num$ 。
- 确认区间后,该元素编号减去之前区间的元素总个数即为该元素在该区间内的编号,即 $num = i - pre\_sum[seg\_num-1]$ 。
- 观察每个区间,因为是反复折返一个点,所以该区间内的奇数元素大小都等于 $seg\_num$,偶数元素大小等于 $seg\_num + num / 2$ 。
- 最后特判 $i > pre\_sum[n-1] return 1$ 即可。
代码
#include <bits/stdc++.h> using ll = long long; using namespace std; const int M = 1e5+100; ll n, l, r, pre_sum[M]; int cal(ll x) { if (x > pre_sum[n - 1]) return 1; int seg_num = lower_bound(pre_sum, pre_sum + n, x) - pre_sum; int num = x - pre_sum[seg_num - 1]; if (num & 1) return seg_num; else return seg_num + num / 2; } void solve() { cin >> n >> l >> r; for (int i = 1; i < n; i++) { pre_sum[i] = pre_sum[i - 1] + 2 * (n - i); } for (ll i = l; i <= r; i++) { cout << cal(i) << " "[i==r]; } } int main() { int t; cin >> t; while (t--) solve(); }
D题代码参考自:MiFaFaOvO(dlstxdy)。