Codeforces Round 922 (Div. 2)
A. Brick Wall
因为水平砖块的长度至少为
参考代码
#include <cstdio> int main() { int t; scanf("%d", &t); while (t--) { int n, m; scanf("%d%d", &n, &m); printf("%d\n", m / 2 * n); } return 0; }
B. Minimize Inversions
注意对
证明:考虑两对元素
时间复杂度为
参考代码
#include <cstdio> const int N = 200005; int idx[N], b[N]; int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); idx[x] = i; } for (int i = 1; i <= n; i++) scanf("%d", &b[i]); for (int i = 1; i <= n; i++) printf("%d%c", i, i == n ? '\n' : ' '); for (int i = 1; i <= n; i++) printf("%d%c", b[idx[i]], i == n ? '\n' : ' '); } return 0; }
C. XOR-distance
考虑
不妨设
参考代码
#include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int LOG = 60; int main() { int t; scanf("%d", &t); while (t--) { LL a, b, r, x = 0; scanf("%lld%lld%lld", &a, &b, &r); if (a < b) swap(a, b); bool high = true; for (int i = LOG - 1; i >= 0; i--) { int ai = (a >> i) & 1, bi = (b >> i) & 1; if (ai == 1 && bi == 0) { if (!high && r >= 1ll << i) { x += 1ll << i; r -= 1ll << i; } high = false; } } printf("%lld\n", abs((a ^ x) - (b ^ x))); } return 0; }
D. Blocking Elements
考虑二分答案。因为尝试的分隔代价限定得越小,就越难实现,限定得越大则越有可能实现,满足单调性。
当尝试的分隔代价限定为
时间复杂度为
参考代码
#include <cstdio> #include <deque> using namespace std; typedef long long LL; const int N = 100005; int a[N], n; LL dp[N], sum[N]; bool check(LL x) { deque<int> dq; dq.push_back(0); int idx = 0; for (int i = 1; i <= n + 1; i++) { while (idx <= i && sum[i - 1] - sum[idx] > x) idx++; while (!dq.empty() && dq.front() < idx) dq.pop_front(); dp[i] = dp[dq.front()] + a[i]; while (!dq.empty() && dp[dq.back()] >= dp[i]) dq.pop_back(); dq.push_back(i); } return dp[n + 1] <= x; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); sum[i] = sum[i - 1] + a[i]; } a[n + 1] = 0; sum[n + 1] = sum[n]; LL ans, l = 1, r = sum[n]; while (l <= r) { LL mid = (l + r) / 2; if (check(mid)) { r = mid - 1; ans = mid; } else l = mid + 1; } printf("%lld\n", ans); } return 0; }
F. Caterpillar on a Tree
首先可以发现,要想遍历整棵树,实质上就是遍历到每一个叶子结点,并且将传送门用在非叶子结点以外的地方没有意义。
遍历的最优路径可以被分成三个部分:根结点移动到叶子结点、从一个叶子结点移动到另一个叶子结点、从叶子结点传送到根结点。为了简化操作,不妨认为一开始有
那么如何安排遍历叶子结点的顺序呢?这里的策略是对于每个结点按照每棵子树深度大小升序来排序,然后依次遍历。感性理解:让深度最深的叶子结点作为遍历一棵子树的最后一个结点,这样如果有传送机会可以尽可能减少向上返回的步数。
对于每个叶子结点,计算其到下一个叶子结点如果使用传送门可以节省的花费。这里不需要真的去实现 LCA 算法,只需要向上跳,直到跳到某个不作为其父节点下最深子树的根结点的结点即可停下,因为对于这样的结点,它与下一个要去叶子结点之间的最近公共祖先就是此时的父节点。
时间复杂度为
参考代码
#include <cstdio> #include <vector> #include <algorithm> using namespace std; const int N = 200005; int p[N], d[N], h[N], save[N]; vector<int> tree[N]; bool cmp(int x, int y) { return d[x] < d[y]; } void dfs(int cur, int fa) { d[cur] = 0; h[cur] = h[fa] + 1; for (int child : tree[cur]) { dfs(child, cur); d[cur] = max(d[cur], d[child]); } sort(tree[cur].begin(), tree[cur].end(), cmp); d[cur]++; } int main() { int n, k; scanf("%d%d", &n, &k); for (int i = 2; i <= n; i++) { scanf("%d", &p[i]); tree[p[i]].push_back(i); } dfs(1, 0); int len = 0; for (int i = 1; i <= n; i++) { if (tree[i].size() == 0) { int res = 0, cur = i; while (cur != 1) { res++; if (cur == tree[p[cur]].back()) { cur = p[cur]; } else { save[++len] = res - h[p[cur]] + 1; break; } } if (cur == 1) save[++len] = res; } } sort(save + 1, save + len + 1); int ans = 2 * (n - 1); k++; // 将遍历到最后一个叶子结点后结束看做是用了一次传送门 for (int i = len; i >= 1; i--) { if (save[i] <= 0 || k == 0) break; ans -= save[i]; k--; } printf("%d\n", ans); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?