Codeforces Round 894 (Div. 3)
A.
小细节的题。时间复杂度
view
#include <bits/stdc++.h> #define REP(i, A, N) for (int i = (int)A; i <= (int)N; i++) #define PER(i, N, A) for (int i = (int)N; i >= (int)A; --i) typedef long long ll; const int MAXN = 200005; char s[25][25]; int n, m; bool check() { std::string v = "vika?"; // 逐个查找存储txt串标记指针 int l = 0; REP(j, 1, m) { REP(i, 1, n) { if (s[i][j] == v[l]) { l++; break; // 查找到及时 break } } } return l == 4; } void solve() { std::cin >> n >> m; REP (i, 1, n) REP(j, 1, m) std::cin >> a[i][j]; std::cout << (check() ? "YES" : "NO") << '\n'; } signed main() { std::ios::sync_with_stdio(false); std::cin.tie(0); #ifndef ONLINE_JUDGE freopen("IO/in", "r", stdin); freopen("IO/out", "w", stdout); #endif int _ = 1; std::cin >> _; while (_--) { solve(); } return 0; }
B. 有一个长为
- 若
, 一个 。
对于某个
- 注意到非降子串,注意到数形结合。
- 观察到:一段长为
的非降 子串可生成由一段长为 的非降 子串。 - 注意在同一平面对比两个数组的数形结合。
(待补图) - 若
, 一次 ,否则 两次 。
时间复杂度
view
#include <bits/stdc++.h> #define REP(i, A, N) for (int i = (int)A; i <= (int)N; i++) #define PER(i, N, A) for (int i = (int)N; i >= (int)A; --i) typedef long long ll; typedef double db; const double EPS=1e-8; const int ZSX=998244353; const int MAXN = 200005; void solve() { int n; std::cin >> n; std::vector<int> b(n+1); REP(i, 1, n) std::cin >> b[i]; std::vector<int> a(1, b[1]); REP(i, 2, n) { if (b[i] >= b[i - 1]) a.push_back(b[i]); else a.push_back(b[i]), a.push_back(b[i]); } int m = (int)a.size(); std::cout << m << '\n'; REP(i, 0, m - 1) { std::cout << a[i] << " \n"[i == m - 1]; } } signed main() { std::cin.tie(0); std::cout.tie(0); std::ios::sync_with_stdio(false); #ifndef ONLINE_JUDGE freopen("IO/in", "r", stdin); freopen("IO/out", "w", stdout); #endif int _ = 1; std::cin >> _; while (_--) { solve(); } return 0; }
C. 有一个长为
- 注意到数形结合。不难发现仅
的是图形经过 对称的必要条件。此题给出了条件降低难度。 - 在微积分或圆锥曲线以外的领域,一个图形无法通过一个多项式表示。此时需要从更
的角度考虑图形对称问题。 - 考虑计算
轴上的贡献 ,每次 区间加 的贡献,这是一个经典的差分问题。扫描 轴, 时图形关于 对称。数组越界一定不对称,可以特判避免。
时间复杂度
view
#include <bits/stdc++.h> #define REP(i, A, N) for (int i = (int)A; i <= (int)N; i++) #define PER(i, N, A) for (int i = (int)N; i >= (int)A; --i) typedef long long ll; typedef unsigned long long ull; typedef double db; const double EPS=1e-8; void solve() { int n; std::cin >> n; std::vector<ll> a(n+1), b(n + 2); bool ok = true; REP(i, 1, n) { std::cin >> a[i]; if (a[i] > n) ok = false; else { b[1] += 1; b[ a[i] + 1 ] -= 1; } } REP(i, 1, n) b[i] += b[ i - 1 ]; REP(i, 1, n) ok &= (b[i] == a[i]); std::cout << (ok ? "YES" : "NO") << '\n'; } signed main() { std::cin.tie(0); std::ios::sync_with_stdio(false); #ifndef ONLINE_JUDGE freopen("IO/in", "r", stdin); freopen("IO/out", "w", stdout); #endif int _ = 1; std::cin >> _; while (_--) { solve(); } return 0; }
D. 一个大小为
- 多重集合中每个元素都不一样时,可以获得最多方案数。于是可以二分到
满足 。 - 任意加入
个各自不同且集合内已有的元素后,有 种组合方案。- 证明:
- 任意加入一个集合外的元素,
,不合法。 - 第一次任意加入一个集合内的元素
,方案数加一,即 。重复加入不会更优
- 任意加入一个集合外的元素,
- 证明:
- 答案为
。
时间复杂度
view
#include <bits/stdc++.h> typedef long long ll; void solve() { ll n; std::cin >> n; ll L = 0 - 1, R = 2E9 + 1; while ( R - L > 1 ) { ll M = ( R + L ) >> 1; ll cost = M * (M - 1) / 2; if (cost > n) R = M; else L = M; // L } std::cout << L + n - L * (L - 1) / 2 << '\n'; } int main() { int _ = 1; std::cin >> _; while (_--) { solve(); } }
E. 给一个长为
- 转化:
必须由一化二(经典拆分才能观察到性质),转化为 ,后可求解。当前选择第 次。 - 于是
- 发现限制住
后 具有单调性,故枚举 。 - 维护前
个最大值的经典做法是小根堆维护。同时可以维护其和。 - 注意到此处是需要维护前不超过
个最大值,特殊在于- 做法与维护前
个最值相差不大 - 不需要等于
时即可更新答案 - 如果可能更优,则允许不选。不选负数
更大,于是更优。
- 做法与维护前
时间复杂度
view
#include <bits/stdc++.h> #define REP(i, A, N) for (int i = (int)A; i <= (int)N; i++) #define PER(i, N, A) for (int i = (int)N; i >= (int)A; --i) typedef long long ll; void solve() { int n, m, d; std::cin >> n >> m >> d; std::vector<int> a(n+1); std::priority_queue<int, std::vector<int>, std::greater<int> > q; ll ans = 0, sum = 0; REP(i, 1, n) { std::cin >> a[i]; if (a[i] > 0) { q.push(a[i]); sum += a[i]; } if (q.size() > m) { sum -= q.top(); q.pop(); } ans = std::max(ans, sum - 1LL * d * i); } std::cout << ans << '\n'; } signed main() { std::cin.tie(0); std::ios::sync_with_stdio(false); #ifndef ONLINE_JUDGE freopen("IO/in", "r", stdin); freopen("IO/out", "w", stdout); #endif int _ = 1; std::cin >> _; while (_--) { solve(); } return 0; }
F. 有
- 显然地让一部分重量为
的物品用 类魔法消灭,另一部分重量为 的物品用 类魔法消灭。此时花费时间为 。枚举 即可找到最小 。 - 一堆物品的总重量确定,某个
是否是其中 个物品的和。当重量和不大,物品个数较多时,是一个时间复杂度 的 背包问题。
view
#include <bits/stdc++.h> typedef long long ll; void solve() { int w, f; std::cin >> w >> f; int n; std::cin >> n; std::vector<int> a(n+1); int sum = 0; for (int i = 1; i <= n; i++) { std::cin >> a[i]; sum += a[i]; } std::vector<int> dp(sum+1); dp[0] = 1; for (int i = 1; i <= n; i++) { for (int j = sum; j >= a[i]; --j) { dp[j] |= dp[j - a[i]]; } } int cost = 1 << 30; for (int i = sum; ~i; --i) if (dp[i]) { cost = std::min(cost, std::max((i + w - 1) / w, (sum - i + f - 1) / f)); } std::cout << cost << '\n'; } int main() { int _ = 1; std::cin >> _; while (_--) { solve(); } }
G. 本场中质量最高且高得离谱的题。
一个数组
- 按非降排序并去重
- 当数组中只有一个元素时输出,终止程序
- 让数组中第
个数加上 , 是现在数组的长度,下标从 开始。 - 返回步骤 1
现在测试
- 将第
个数改为 ,即 。 - 将
输入程序,输出执行结果。
此题的主要考点:“有序数组”和“去重数组”的性质。
- 观察一:一个升序数组,加上
后,显然其差分数组整体减 。 - 观察二:每个相邻数字的差分会减
,差分为 会被去重。设一开始最大的差分为 ,于是总共会迭代 轮。 - 观察三:去重数组在某些位置一直加数,最大值会留到最后,只需关心最大值变化。
- 观察四:若最大值一开始为
,每次程序迭代, 恒为 。 - 于是程序输出的答案可
求解,为 。
此题的第二个考点:数据结构。
新问题,如何创造数据结构。支持单点修改,维护一个数组的最大值和最大差分。
这是两个都能用
- 单点删除、单点查询显然可以
。 - 因为要维护数组而不是下标,所以避免去重,使用
。 - 差分可以用各元素求得。实际上我们只是用一个数据结构维护数组的所有元素,顺便在另一个数据结构里把差分维护了。类似多树状数组维护。
的功能可以平替 。
注意单点修改对数组差分的影响:
- 修改可以转化为删除、插入。
- 删除或插入一个元素,对一个数组的差分有会影响三次。
注意
会把重复 的迭代器都删掉 一个元素的迭代器只会删除一个 的迭代器。
注意
- 若
在首位,则 。 - 若
在末位,则 。
接下来是默认已经初始化后的操作。
- 单点修改
,维护最大值:
。
,在 中 到一个 的迭代器,将这个迭代器删除。
,对 一个 。 - 单点修改
,维护最大差分:(定义 ,数据结构中通常只维护 到 共 个差分,因为 和 不代表元素相对关系,除非是数学类算贡献的题)
,且需借助到 。
。- 在
中 到 的迭代器 。 - 若
不在 首位, 。 - 若
不在 末位, 。 - 若
既不在 首位也不在 末位, 。 - 在
中 迭代器 。
- 在
。- 在
中 元素 ,并返回迭代器 。 - 若
不在 首位, 。 - 若
不在 末位, 。 - 若
既不在 首位也不在 末位, 。
- 在
view
#include <bits/stdc++.h> typedef long long ll; void solve() { int n; std::cin >> n; std::vector<int> a(n+1); std::multiset<int> c1, c2{0}; auto add = [&](int x) { auto t = c1.insert(x); if (t != c1.begin()) { c2.insert( *t - *std::prev(t) ); } if (std::next(t) != c1.end()) { c2.insert( *std::next(t) - *t ); } if (t != c1.begin() && std::next(t) != c1.end()) { c2.erase( c2.find( *std::next(t) - *std::prev(t) ) ); } }; auto del = [&](int x) { auto t = c1.find(x); if (t != c1.begin()) { c2.erase( c2.find( *t - *std::prev(t) ) ); } if (std::next(t) != c1.end()) { c2.erase( c2.find( *std::next(t) - *t ) ); } if (t != c1.begin() && std::next(t) != c1.end()) { c2.insert( *std::next(t) - *std::prev(t) ); } c1.erase(t); }; for (int i = 1; i <= n; i++) { std::cin >> a[i]; add(a[i]); } int q; std::cin >> q; std::vector<int> ans(q+1); for (int i = 1; i <= q; i++) { int id, x; std::cin >> id >> x; del(a[id]); a[id] = x; add(x); ans[i] = *c1.rbegin() + *c2.rbegin(); } for (int i = 1; i <= q; i++) { std::cout << ans[i] << " \n"[i==q]; } } int main() { int _ = 1; std::cin >> _; while (_--) { solve(); } }
——永远是挑战而不是练习,下次一定更好。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】