这道题目难点主要是读懂题意,然后按照题意手玩一下就出来了。
按照题目手玩一下样例就可以找到规律,在采取最优分组策略的情况下,能够影响的结果的就是比自己分数的低的人的个数。
当进入 32 强后,如果有2个人比你的分数低,你就晋级。
当进入 16 强后,有两种情况,在你自己的组排第一,要有3 个人比你低,你要赢得另一组的第二,还有3个人比你低。另一种情况是在你的组排第二,你要比 2 个人要高,同时你要比另一组的第一高,因此还有4 个人比你低。所以晋级的条件是有 6 个人比你分数低。
进入 8 强后,你本身比 6 个人分数高,你要赢另一个人,同时他也比 6 个人高,因此晋级条件是比 13 个人分数高。
进入 4 强后,推理同 8 强你要比 27 个人高。
进入决赛后,很简单比所有人都高,也就是 31 个人。
可以想到的是,一定是吧最小值一点点变大,这样是最优的。
那么我们就应该假设我们找到了最小值,并且最小值是连续的一段[l,r]我们就应该找到这一段的左侧和右侧,选取较小值吧这一段变大,产生的贡献就是r−1+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;
}
先用对顶堆求出b数组。
然后我们可以二分答案,二分枚举x,然后把b数组内小于等于x的比标记为1。然后用前缀和维护一下,当我需要计算c数组的时候就可以O(1)的计算出区间内总数和小于等于x的数量,因此可以判断出当前的c是否大于等于x。然后我们在统计c种大于等于x的个数,如果超过了一半,说明中位数大于等于x。
用map<string,set<string>>
统计一下每道题目的通过人数就好了。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律