1 条题解
-
0
自动搬运
来自洛谷,原作者为

沉石鱼惊旋
已完成今日我对着铁质的凳子踢了五下然后把那堆钢栏杆在地上滚了一下然后把椅子夹在了篮筐上大学习搬运于
2025-08-24 23:11:32,当前版本为作者最后更新于2025-03-27 19:16:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意
一棵 个节点的有根树,以 为根。每个点 有一个 。对于所有 , 的父亲 总是满足 。
你可以给出 的一对 调用交互库 查询 。
还原排列。
设 表示这棵树所有可能的 的个数,,你调用了 次交互库,令 。当 的时候,如果 ,那么 ,否则 。只有 时才能获得满分。
。
做法
设 表示 子树内排好序的序列。
初始 为空,之后一直加入 。把 和 合并起来。全部合并完之后把 插到 开头。
考虑优化这个插入的部分。
一个想法是分块,设合并的序列是 ,长度 且 。块长设为 。然后取出 每个块第一个元素和 归并排序。最后在块内二分出这个元素最后的位置。
这个做法需要 $\lceil L_1/B \rceil+L_2-1+L_2\times (\lceil\log_2(B-1)\rceil+1)$ 次操作。
考虑尽量把这个 卡到 。这样块内二分次数不变。但是归并次数可以变少。
直接这样做分够高了但是过不去。大概有八十多分。
如果 元素很少 元素很多我们还有 次操作的做法。就是直接把整个序列看成一块直接二分(不用取出第一个元素)。
分块策略枚举块长算一个最优解出来,和第二种做法次数比较一下,选择次数更小的策略。这里次数写出来的式子要卡精细一点。
代码
https://qoj.ac/submission/951035
#include <bits/stdc++.h> #include "evolution2.h" using namespace std; vector<int> recover(int N, vector<int> U, vector<int> V) { int n = N; vector<int> p; vector<vector<int>> a(n); vector<vector<int>> vec(n); for (int i = 0; i < n - 1; i++) { auto [u, v] = tie(U[i], V[i]); a[u].emplace_back(v); a[v].emplace_back(u); } auto merge = [&](vector<int> &a, vector<int> &b) { if (a.empty()) return b; if (b.empty()) return a; if (a.size() < b.size()) swap(a, b); vector<int> t; int cnt = a.size() + b.size() - 1, l = 0; auto upd = [&](int len) { int times = (a.size() + len) / (len + 1) + b.size() - 1 + b.size() * (__builtin_ctz(len) + 1); if (times < cnt) cnt = times, l = len; }; int len = 1; upd(len); while (len + 1 < a.size()) { len <<= 1; upd(len); } len = l + 1; l = 1; while (l < a.size() + 1) l <<= 1; vector<int> v; int i = 0, j = 0; vector<int> pos(b.size()); if (b.size() * __builtin_ctz(l) < cnt) { for (int p = 0; p < b.size(); p++) { pos[p] = !p ? -1 : pos[p - 1]; int l = !p ? 0 : pos[p - 1] + 1, r = a.size() - 1; while (l <= r) { int mid = l + r >> 1; if (compare(a[mid], b[p])) l = (pos[p] = mid) + 1; else r = mid - 1; } if (pos[p] == -1) v.emplace_back(b[p]); } } else { for (int i = 0; i < a.size(); i += len) t.emplace_back(a[i]); int c = 0; while (i < t.size() || j < b.size()) { if (i == t.size()) v.emplace_back(-(++j)); else if (j == b.size()) v.emplace_back((i++) * len); else if (compare(t[i], b[j])) v.emplace_back((i++) * len); else v.emplace_back(-(++j)); } int lst = -1; t = v; v.clear(); for (int p : t) { if (p >= 0) lst = p; else { p = -p - 1; if (~lst) { pos[p] = lst; int l = lst + 1, r = min((int)a.size() - 1, lst + len - 1); while (l <= r) { int mid = l + r >> 1; c++; if (compare(a[mid], b[p])) l = (pos[p] = mid) + 1; else r = mid - 1; } } else { pos[p] = -1; v.emplace_back(b[p]); } } } } i = 0, j = 0; while (pos[j] == -1) j++; while (i < a.size() || j < b.size()) { if (i == a.size()) v.emplace_back(b[j++]); else if (j == b.size()) v.emplace_back(a[i++]); else if (i <= pos[j]) v.emplace_back(a[i++]); else v.emplace_back(b[j++]); } return v; }; auto dfs = [&](auto self, int u, int fa) -> void { for (int v : a[u]) { if (v == fa) continue; self(self, v, u); vec[u] = merge(vec[u], vec[v]); } vec[u].insert(vec[u].begin(), u); }; dfs(dfs, 0, -1); p = vec[0]; vector<int> q(n); for (int i = 0; i < n; i++) q[p[i]] = i; return q; }
- 1
信息
- ID
- 11737
- 时间
- 3000ms
- 内存
- 2048MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者