2023牛客寒假算法基础集训营5 A-L
A
题解
知识点:前缀和,二分。
找到小于等于 的最后一个物品,往前取 个即可,用前缀和查询。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int a[100007]; ll sum[100007]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, q; cin >> n >> q; for (int i = 1;i <= n;i++) cin >> a[i]; sort(a + 1, a + n + 1); for (int i = 1;i <= n;i++) sum[i] = sum[i - 1] + a[i]; while (q--) { int k, x; cin >> k >> x; int pos = upper_bound(a + 1, a + n + 1, x) - a - 1; cout << sum[pos] - sum[max(0, pos - k)] << '\n'; } return 0; }
B
题解
知识点:博弈论。
显然,每次取 是最优策略,但先拿的人串长肯定大于等于后拿的人,因此偶数平局,奇数后手胜。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; if (n & 1) cout << "Yaya-win!" << '\n'; else cout << "win-win!" << '\n'; return 0; }
C
题解
知识点:数学。
分类讨论:
-
串长相等时,只有 时结果是
=
,否则找到第一个不同的位置可以任意修改大小关系,结果是!
。 -
串长不同时,不妨假设 比 长,那么首先就一定有 的情况,考虑 最小的情况会不会造成 。
若 超出 的部分不是相同的数,则一定不能全变成前导 此时无论如何一定有 ,结果为
>
;否则,先 超出部分全部变成前导 ,此时 长度相同。之后,若 则结果为
!
。否则,找到第一对不同的数,如果 的数是 前导数,则 无论如何都有 ,结果为>
,否则结果为!
。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); string a, b; cin >> a >> b; if (a.size() == b.size()) { if (a == b) cout << "=" << '\n'; else cout << "!" << '\n'; return 0; } char ans = '>'; if (a.size() < b.size()) { swap(a, b); ans = '<'; } char zero = a[0]; for (int i = 0;i < a.size() - b.size();i++) { if (a[i] != zero) { cout << ans << '\n'; return 0; } } a.erase(a.begin(), a.begin() + a.size() - b.size()); if (a == b) { cout << "!" << '\n'; return 0; } for (int i = 0;i < a.size();i++) { if (a[i] != b[i]) { if (b[i] == zero) cout << ans << '\n'; else cout << "!" << '\n'; return 0; } } return 0; }
D
题解
知识点:优先队列,枚举。
维护一个过关顺序,按照区间左端点从小到大排序即可,每次看看左端点最小的是不是小于等于自己在位置加 ,然后更新右端点。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; pair<int, int> a[100007], b[100007]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i].first >> a[i].second; for (int i = 1;i <= n;i++) cin >> b[i].first >> b[i].second; priority_queue <pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq1, pq2; int pos1 = 0, pos2 = 0; for (int i = 1;i <= n;i++) { pq1.push(a[i]); pq2.push(b[i]); while (pq1.size() && pq1.top().first <= pos1 + 1) { pos1 = max(pos1, pq1.top().second); pq1.pop(); } while (pq2.size() && pq2.top().first <= pos2 + 1) { pos2 = max(pos2, pq2.top().second); pq2.pop(); } if (pos1 > pos2) { cout << "sa_win!" << '\n'; cout << pos1 - pos2 << '\n'; } else if (pos1 < pos2) { cout << "ya_win!" << '\n'; cout << pos2 - pos1 << '\n'; } else { cout << "win_win!" << '\n'; cout << 0 << '\n'; } } return 0; }
E
题解
知识点:构造。
考虑将排列 ,这样每个数必然和其他数相遇一次。
每次交换全部顺序对,一定保证在 轮内完成交换。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; vector<int> v(n + 1); iota(v.begin(), v.end(), 0); vector<vector<pair<int, int>>> ans; while (!is_sorted(v.begin() + 1, v.end(), greater<int>())) { vector<pair<int, int>> t; for (int i = 1;i < n;i++) { if (v[i] < v[i + 1]) { swap(v[i], v[i + 1]); t.push_back({ v[i],v[i + 1] }); i++; } } ans.push_back(t); } cout << ans.size() << '\n'; for (auto i : ans) { cout << i.size() << '\n'; for (auto [x, y] : i) cout << x << ' ' << y << '\n'; } return 0; }
F
题解
方法一
知识点:单调栈,贪心。
因为根据字典序,所以我们要尽可能使前面的字母更大。
考虑用单调栈维护前缀字典序最大的子序列,同时需要维护 ,若 则不能再弹出元素。若到最后 ,则还可以对得到的子序列从后往前弹出,可能使字典序更大。将弹出的元素计数,最后从大到小一并加到子序列后面。
时间复杂度
空间复杂度
方法二
知识点:枚举,贪心。
预处理 表示字符串 的位置中字母 第一个出现的位置。随后只需要从大到小枚举 ,找到最大的且能操作到的字母 ,将 到 的所有元素全部操作一遍,并计数。
处理出来的是字典序最大的子序列,此时若 还有剩下的,那就从后往前依次弹出子序列元素,同样计数。
最后将跳过的或弹出的元素从大到小安排到子序列后面即可。
时间复杂度
空间复杂度
代码
方法一
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, k; cin >> n >> k; string s; cin >> s; array<int, 26> cnt{}; string ans; for (auto ch : s) { while (k && ans.size() && ans.back() < ch) { cnt[ans.back() - 'a']++; ans.pop_back(); k--; } ans += ch; } while (k && ans.size()) { cnt[ans.back() - 'a']++; ans.pop_back(); k--; } for (int i = 25;i >= 0;i--) ans += string(cnt[i], i + 'a'); cout << ans << '\n'; return 0; }
方法二
#include <bits/stdc++.h> using namespace std; using ll = long long; int nxt[100007][26]; int cnt[26]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, k; cin >> n >> k; string s; cin >> s; for (int i = 0;i < 26;i++) nxt[n][i] = -1; for (int i = n - 1;i >= 0;i--) { for (int j = 0;j <= 25;j++) nxt[i][j] = nxt[i + 1][j]; nxt[i][s[i] - 'a'] = i; } string ans; for (int i = 0, nnxt;i < s.size(); i = nnxt + 1) { for (int j = 25;j >= 0;j--) { if (~nxt[i][j] && nxt[i][j] - i <= k) { nnxt = nxt[i][j]; break; } } for (int j = i;j < nnxt;j++) cnt[s[j] - 'a']++; ans += s[nnxt]; k -= nnxt - i; } while (k && ans.size()) { cnt[ans.back() - 'a']++; ans.pop_back(); k--; } for (int i = 25;i >= 0;i--) if (cnt[i]) ans += string(cnt[i], i + 'a'); cout << ans << '\n'; return 0; }
G
题解
知识点:二分图,图匹配。
对于每个需要填的位置,由两侧已知的数字可以得到两个待选的数字,如 ,除了最后一个空位因为只需要和第 个数字有一位不同,所以有更多可能。
注意到每个数字只能选一次,换句话说,空位和数字是一一配对关系。考虑画成二分图,题目保证一定有解,所以只要匹配一次即可。可以使用匈牙利算法或者网络流求解二分图匹配,这里采用匈牙利算法。
直接使用复杂度是跑满 的(但是交了也能过?),根本原因是每次跑完一个点的增广路,需要把访问标记全清空,复杂度是 的。但实际上没有必要,考虑对于某次增广失败的所有右部点,他们的左部配对没有任何变化,无论你用哪个其他的左部点去配对这些增广失败的右部点,这些右部点都会走原来的路径,而原来的路径依旧是失败的,根本没必要再走一次。因此,我们每次配对的时候,如果某个右部点增广失败了,就保留访问标记,不需要清空,而只清空成功的增广路上的所有访问标记,这个只需要在dfs最后加上清空即可,能让算法跑的巨快。
特别地在这道题,每个点最多被访问四次就会被永久标记,复杂度变成线性。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; struct Graph { struct edge { int v, nxt; }; int idx; vector<int> h; vector<edge> e; Graph(int n = 0, int m = 0) { init(n, m); } void init(int n, int m) { idx = 0; h.assign(n + 1, 0); e.assign(m + 1, {}); } void add(int u, int v) { e[++idx] = { v,h[u] }; h[u] = idx; } }; const int N = (1 << 16) + 7, M = N; Graph g; int n; int a[N]; int p[M], vis[M]; bool dfs(int u) { for (int i = g.h[u];i;i = g.e[i].nxt) { int v = g.e[i].v - n - 1; if (vis[v]) continue; vis[v] = 1; if (p[v] && !dfs(p[v])) continue; p[v] = u; vis[v] = 0; return true; } return false; } int Hungary() { int cnt = 0; for (int i = 2;i <= n;i += 2) cnt += dfs(i); return cnt; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n; g.init(2 * n, 2 * n + n); for (int i = 1;i <= n;i += 2) cin >> a[i], vis[a[i]] = 1; for (int i = 2;i < n;i += 2) { int t = a[i - 1] ^ a[i + 1]; int x1 = a[i - 1] ^ (t & -t); t -= t & -t; int x2 = a[i - 1] ^ (t & -t); if (!vis[x1]) g.add(i, x1 + n + 1); if (!vis[x2]) g.add(i, x2 + n + 1); } for (int i = 0;i < 16 && (1 << i) <= a[n - 1];i++) { int x = a[n - 1] ^ (1 << i); if (!vis[x]) g.add(n, x + n + 1); } Hungary(); for (int i = 0;i < n;i++) if (p[i]) a[p[i]] = i; for (int i = 1;i <= n;i++) cout << a[i] << ' '; cout << '\n'; return 0; }
H
题解
知识点:模拟。
直接按照要求模拟即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int x, y, k, n; ll t; cin >> x >> y >> k >> n >> t; ll sum = 0, cnt = 0, nowc = x; for (int i = 1;i <= n;i++) { sum += nowc * (n - i + 1); cnt += n - i + 1; nowc = x + cnt / k * y; if (sum >= t) { cout << i << '\n'; return 0; } } cout << -1 << '\n'; return 0; }
I
题解
知识点:贪心。
为了保证一定不会输,我们从后往前考虑。
若最后一局才一赢,但赢回本至少需要留 ,但我们希望在前面赢的多,因此我们留最少的 ,则剩下 ;若在 局才赢,那么至少需要留除去第 局剩下部分的一半取上整,同样的我们留最少的就行,以此类推,最后剩下的所有给第一局,若中途不够分,则无解。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; ll a[100007]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; ll m; cin >> n >> m; for (int i = n;i >= 1;i--) { if (!m) { cout << -1 << '\n'; return 0; } if (i == 1) a[i] = m; else { a[i] = (m + 1) / 2; m -= (m + 1) / 2; } } for (int i = 1;i <= n;i++) cout << a[i] << " \n"[i == n]; return 0; }
J
题解
知识点:构造
属于灵光一现题qwq。
我们需要尽可能构造一条单向通道,这样才能保证尽可能多的格子都会被踩到。
可以考虑螺旋形,先考虑偶数情况,例如 从 出发向右:
注意到每次经过下划线处会加 。
奇数情况取偶数情况左下角矩形即可,出发点在 。
具体实现可以看代码。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int a[1007][1007]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; bool odd = n & 1; if (odd) cout << n << ' ' << n << '\n', n++; else cout << 1 << ' ' << 1 << '\n'; int x = 1, y = 1, cur = 0; for (int i = 1;i <= n / 2;i++) { for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) { a[x][y] = cur; if (y == n / 2) (cur += 1) %= 3; y++; } for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) { a[x][y] = cur; if (x == n / 2) (cur += 1) %= 3; x++; } for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) { a[x][y] = cur; y--; } for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) { a[x][y] = cur; x--; } x++, y++; } if (odd) n--; for (int i = 1 + odd;i <= n + odd;i++) for (int j = 1;j <= n;j++) cout << a[i][j] << " \n"[j == n]; cout << n * n - 1 << '\n'; return 0; }
K
题解
知识点:贪心。
每次最多可以淘汰 个人,只要取 即可。
特别地,若 ,则一定无法继续淘汰人。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); ll x; cin >> x; int cnt = 0; while (x > 2) { x = x / 2 + 1; cnt++; } cout << cnt << '\n'; return 0; }
L
题解
知识点:线性dp。
设 为剩余 个人的最小花费。初始状态为 其他无穷大,转移方程见代码。
最后从小到大遍历一次就能获得人数最小的最小花费。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int b[507], x[507]; ll f[100007]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; for (int i = 1;i <= m;i++) cin >> b[i] >> x[i]; for (int i = 1;i <= n;i++) f[i] = 1e18; f[n] = 0; for (int i = n;i >= 3;i--) { if (f[i] >= 1e18) continue; for (int j = 1;j <= m;j++) { if (i >= x[j]) f[i / x[j] * x[j]] = min(f[i / x[j] * x[j]], f[i] + b[j]); } } for (int i = 1;i <= n;i++) { if (f[i] < 1e18) { cout << f[i] << '\n'; break; } } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17099114.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
2022-02-07 计数问题