TypeDB Forces 2023 (Div. 1 + Div. 2, Rated, Prizes!) A-E
A
题意
给一个正整数 ,找到一组正整数 ,满足 。
题解
知识点:数学。
尝试 ,显然此时 ,如果 为偶数可以直接求得。
又左式无论 奇偶性结果都是偶数,因此 为奇数时无解。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int n; cin >> n; if (n & 1) return false; else cout << 1 << ' ' << n / 2 << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
B
题意
给定一个正整数 ,将其分解为 ,其中 是不同素数的乘积。
问 的最大值。
题解
知识点:分解质因数,贪心。
先考虑将 质因数分解 ,其中 为素数。
显然,我们可以将不同的素数 合并成一个新的数 ,这样的答案更优,因此我们考虑合并能合并不同的素数。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; void get_pfactor(int n, vector<pair<int, int>> &pfactor) { for (int i = 2;i * i <= n;i++) { if (!(n % i)) { pfactor.push_back({ i,0 }); while (!(n % i)) n /= i, pfactor.back().second++; } } if (n > 1) pfactor.push_back({ n,1 });//最后可能留一个大于sqrt(n)的质数 } bool solve() { int n; cin >> n; vector<pair<int, int>> pfactor; get_pfactor(n, pfactor); vector<ll> v(32, 1); for (auto [x, y] : pfactor) v[y] *= x; ll ans = 0; for (int i = 30;i >= 1;i--) v[i] *= v[i + 1], ans += (v[i] > 1) * v[i]; cout << ans << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
C
题意
给定一组数 和 ,将 分解为新的两个非负整数 且满足 。
求 的最小值。
题解
知识点:线性dp,贪心。
对于 满足的不等式,可以理解为 要同时大于等于 或小于等于 。
考虑对 贪心地分解为一个最小值和一个最大值,即当 ,可以分解为 ;当 ,可以分解为 。可以如此证明,考虑某一段 ,无论 如何, 的极值点一定是在最小值或最大值处,而且这种选择没有后效性,因此分解只需要分解为一个最小值和一个最大值。
接下来我们需要确定最小值和最大值的顺序,考虑设 表示 中 是最小值/最大值时 的最小值,我们可以通过 来转移。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int n, s; cin >> n >> s; vector<pair<int, int>> p(n + 1); for (int i = 1;i <= n;i++) { int x; cin >> x; if (i == 1) p[1] = { 1e9,x }; else if (i == n) p[n] = { x,1e9 }; else { if (x >= 2 * s) p[i] = { s,x - s }; else p[i] = { max(0,x - s),min(x,s) }; } } array<ll, 2> f{}; f[0] = f[1] = 0; for (int i = 2;i <= n;i++) { array<ll, 2> g{}; auto [x1, y1] = p[i - 1]; auto [x2, y2] = p[i]; g[0] = min(f[0] + 1LL * y1 * x2, f[1] + 1LL * x1 * x2); g[1] = min(f[0] + 1LL * y1 * y2, f[1] + 1LL * x1 * y2); f = g; } cout << f[0] << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
D
题意
给定 个数 ,从第 个数出发,每次可以跳到第 个数,直到跳到 或 的地方游戏结束。
现在可以选择一个数字改成 的任意一个数字,问有多少种改变方式(不改也算一种),能使得游戏结束。
题解
知识点:dfs,枚举。
我们可以把跳转路径画成一张有向图, 作为游戏结束的点,那么 与 连通表示游戏一定可以结束。
考虑两类点:
- 从 出发能经过的点。
- 与 无关的其他点。
对于第二类点,改变他们并不影响 到终点的连通状态。因此,当 初始已经合法,那么他们能直接贡献 个改变方案,否则没有任何贡献。
对于第一类点,我们需要注意,改变他们可能使得 无解或路径成环。除去 种直接导向 点的方案,他们只能连接那些能到达 且不会使路径成环的点。
对此,我们先给第一类点标记一个到 的距离(无穷大表示不可达)。第一类点可以导向距离比他小的第一类点,而反之一定成环。对于第二类点,如果他们能导向某个第一类点,那么我们可以认为他们的距离和这个第一类点是一样的,因为他们能不能使得路径成环,取决于他们导向的那个第一类点的距离大小,于是我们可以用相同的策略判断第二类点是否可以被导向。因此,我们把可达的点的距离排个序,每次可以二分查找得到合法点的个数
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int n; int a[200007]; bool vis[200007], vis2[200007]; int dis[200007]; int dfs(int u) { if (u < 1 || u > n) return 0; if (vis[u]) return dis[u]; vis[u] = 1; return dis[u] = dfs(u + a[u]) + 1; } int dfs2(int u) { if (u < 1 || u > n) return 0; if (vis[u] || vis2[u]) return dis[u]; vis2[u] = 1; return dis[u] = dfs2(u + a[u]); } bool solve() { cin >> n; for (int i = 1;i <= n;i++) cin >> a[i], dis[i] = 1e9, vis[i] = vis2[i] = 0; dfs(1); for (int i = 1;i <= n;i++) dfs2(i); vector<int> v; for (int i = 1;i <= n;i++) if (dis[i] < 1e9) v.push_back(dis[i]); sort(v.begin(), v.end()); ll ans = 0; for (int i = 1;i <= n;i++) { if (vis[i]) ans += n + 1 + (lower_bound(v.begin(), v.end(), dis[i]) - v.begin()); else if (dis[1] < 1e9) ans += 2 * n + 1; } cout << ans << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
E
题意
给定正整数 ,要求分为恰好 个非空子序列,使得这 个子序列的异或和都为 。
题解
知识点:位运算,贪心,构造。
容易知道,有解的必要条件是 个 的异或和等于 的异或和。满足必要条件后,我们尝试划分序列。
因为满足了必要条件,那我们如果能分出合法序列,那么序列个数的奇偶性一定和 相同,而其他数的异或和一定为 。
我们讨论合法序列个数的最大值情况:
-
最大值大于等于 一定有解,我们可以把多出来的序列和多余的数全部扔进一个序列里,不会改变异或和。
-
最大值小于 个一定无解,因为已经是能得到的最大值了。
于是问题变成如何分出最多的合法序列。我们可以通过这样的方法,在 中:
- 如果有 那就单独分一组。
- 对于 ,如果 ,那就 两个数分一组。
这样就能得到最多的合法序列。
证明:
为了异或得到 ,那么我们一定要保证每个序列都有奇数个数,满足在 的最高位 处也是 。
假设 中有 个数满足在 的最高位 处也是 ,那我们至多可以分 个序列。
对于 个数中任意一个数 , 一定小于 ,所以一定能找到数 与其对应。同时,根据异或运算的等式性质,可以断定 是唯一确定的一对,不会出现一个数被多个数对应,所以我们至少可以分 个序列。
因此我们一定能通过 ( 可以直接安排进一个序列)的方式分出最多的序列,共 个。
得到最多的合法序列后,判断后即可构造或者无解。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int n, k, x; cin >> n >> k >> x; int xsum = 0; for (int i = 1;i <= n;i++) xsum ^= i; if (xsum != x * (k & 1)) return false; vector<char> vis(n + 1); vector<vector<int>> v; for (int i = 1;i <= n;i++) { if (vis[i] || (x ^ i) > n) continue; if (i == x) v.push_back({ i }); else v.push_back({ i,x ^ i }); vis[i] = vis[i ^ x] = 1; } if (v.size() < k) return false; cout << "YES" << '\n'; for (int i = 0;i < k - 1;i++) { cout << v[i].size() << ' '; for (auto val : v[i]) cout << val << ' '; cout << '\n'; } vector<int> r; for (int i = k - 1;i < v.size();i++) for (auto val : v[i]) r.push_back(val); for (int i = 1;i <= n;i++) if (!vis[i]) r.push_back(i); cout << r.size() << ' '; for (auto val : r) cout << val << ' '; cout << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << "NO" << '\n'; } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17080261.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧