The 2024 ICPC Asia East Continent Online Contest (I)

1|0A. World Cup


这道题目难点主要是读懂题意,然后按照题意手玩一下就出来了。

按照题目手玩一下样例就可以找到规律,在采取最优分组策略的情况下,能够影响的结果的就是比自己分数的低的人的个数。

当进入 32 强后,如果有2个人比你的分数低,你就晋级。

当进入 16 强后,有两种情况,在你自己的组排第一,要有3 个人比你低,你要赢得另一组的第二,还有3个人比你低。另一种情况是在你的组排第二,你要比 2 个人要高,同时你要比另一组的第一高,因此还有4 个人比你低。所以晋级的条件是有 6 个人比你分数低。

进入 8 强后,你本身比 6 个人分数高,你要赢另一个人,同时他也比 6 个人高,因此晋级条件是比 13 个人分数高。

进入 4 强后,推理同 8 强你要比 27 个人高。

进入决赛后,很简单比所有人都高,也就是 31 个人。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = vector<int>; void solve() { int n = 32; vi a(n); for (auto &i: a) cin >> i; int cnt = 0; for (int i = 1; i < n; i++) cnt += a[0] > a[i]; if (cnt < 2) cout << "32\n"; else if (cnt < 6) cout << "16\n"; else if (cnt < 13) cout << "8\n"; else if (cnt < 27) cout << "4\n"; else if (cnt < 31) cout << "2\n"; else cout << "1\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while (T--) solve(); return 0; }

2|0F. Make Max


可以想到的是,一定是吧最小值一点点变大,这样是最优的。

那么我们就应该假设我们找到了最小值,并且最小值是连续的一段[l,r]我们就应该找到这一段的左侧和右侧,选取较小值吧这一段变大,产生的贡献就是r1+1

这里我用了类似珂朵莉树的方法,分别按照顺序维护段和大小维护段,然后暴力的模拟就好了,以为每次操作可以使得段的数量至少减1,复杂度O(NlogN)

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = vector<int>; struct seg1 { int l, r, v; seg1(int l, int r = 0, int v = 0) : l(l), r(r), v(v) {} bool operator<(const seg1 &b) const { return l < b.l; } }; struct seg2 { int l, r, v; seg2(int l, int r = 0, int v = 0) : l(l), r(r), v(v) {} bool operator<(const seg2 &b) const { if (v != b.v) return v < b.v; return l < b.l; } }; void solve() { int n; cin >> n; set<seg1> cnt1; set<seg2> cnt2; vi a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; for (int l = 1, r; l <= n;) { r = l; while (r + 1 <= n and a[r + 1] == a[l]) r++; cnt1.emplace(l, r, a[l]); cnt2.emplace(l, r, a[l]); l = r + 1; } int res = 0; while (cnt2.size() > 1) { auto [l, r, v] = *cnt2.begin(); cnt2.erase(cnt2.begin()); cnt1.erase(cnt1.lower_bound(seg1(l, r, v))); res += r - l + 1; auto it = cnt1.lower_bound(seg1(l)); if (it == cnt1.end()) { it = prev(it); auto [nl, nr, nv] = *it; cnt1.erase(it); cnt2.erase(cnt2.lower_bound(seg2(nl, nr, nv))); nl = min(nl, l), nr = max(nr, r), nv = max(nv, v); cnt1.emplace(nl, nr, nv); cnt2.emplace(nl, nr, nv); } else if (it == cnt1.begin()) { auto [nl, nr, nv] = *it; cnt1.erase(it); cnt2.erase(cnt2.lower_bound(seg2(nl, nr, nv))); nl = min(nl, l), nr = max(nr, r), nv = max(nv, v); cnt1.emplace(nl, nr, nv); cnt2.emplace(nl, nr, nv); } else { auto pit = prev(it); if (pit->v == it->v) { auto [nl, nr, nv] = *it; cnt1.erase(it); cnt2.erase(cnt2.lower_bound(seg2(nl, nr, nv))); auto [pl, pr, pv] = *pit; cnt1.erase(pit); cnt2.erase(cnt2.lower_bound(seg2(pl, pr, pv))); nl = min(nl, pl), nr = max(nr, pr); nl = min(nl, l), nr = max(nr, r), nv = max(nv, v); cnt1.emplace(nl, nr, nv); cnt2.emplace(nl, nr, nv); } else { if (pit->v < it->v) it = pit; auto [nl, nr, nv] = *it; cnt1.erase(it); cnt2.erase(cnt2.lower_bound(seg2(nl, nr, nv))); nl = min(nl, l), nr = max(nr, r), nv = max(nv, v); cnt1.emplace(nl, nr, nv); cnt2.emplace(nl, nr, nv); } } } cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while (T--) solve(); return 0; }

3|0G. The Median of the Median of the Median


先用对顶堆求出b数组。

然后我们可以二分答案,二分枚举x,然后把b数组内小于等于x的比标记为1。然后用前缀和维护一下,当我需要计算c数组的时候就可以O(1)的计算出区间内总数和小于等于x的数量,因此可以判断出当前的c是否大于等于x。然后我们在统计c种大于等于x的个数,如果超过了一半,说明中位数大于等于x

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = vector<int>; struct Median { int m, n; priority_queue<int> l; priority_queue<int, vi, greater<>> r; Median() { n = m = 0; } void insert(int x) { n++; if (n == 1) { m = x; return; } if (x <= m) l.push(x); else r.push(x); int t = (n + 1) / 2 - 1; while (l.size() > t) { r.push(m), m = l.top(), l.pop(); } while (l.size() < t) { l.push(m), m = r.top(), r.pop(); } } }; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; vi a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; vector b(n + 1, vi(n + 1)); for (int i = 1; i <= n; i++) { Median median; for (int j = i; j <= n; j++) { median.insert(a[j]); b[i][j] = median.m; } } auto t = [](int k) { k = (1 + k) * k / 2; return (k + 1) / 2; }; auto check = [&](int x) -> bool { vector c(n + 2, vi(n + 2)); for (int j = 1; j <= n; j++) for (int i = j; i >= 1; i--) c[i][j] = (b[i][j] <= x) + c[i + 1][j]; int cnt = 0; for (int l = 1; l <= n; l++) for (int r = l, sum = 0; r <= n; r++) { sum += c[l][r]; if (sum >= t(r - l + 1)) cnt++; } return cnt >= t(n); }; int l = 1, r = 1e9, res = -1; while (l <= r) { int mid = (l + r) / 2; if (check(mid)) res = mid, r = mid - 1; else l = mid + 1; } cout << res; return 0; }

4|0M. Find the Easiest Problem


map<string,set<string>>统计一下每道题目的通过人数就好了。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; void solve() { int n; cin >> n; map<string, set<string>> cnt; for (string team, problem, result; n; n--) { cin >> team >> problem >> result; if (result != "accepted") continue; cnt[problem].insert(team); } string res; int val = 0; for (auto [i, v]: cnt) { if (v.size() > val) val = v.size(), res = i; } cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while (T--) solve(); return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18417549.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(168)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示