P10220 [省选联考 2024] 迷宫守卫 题解
说一下自己赛时做法。赛时会了,但没能调出来,几乎确定进不去队了,留下这篇题解作为这次比赛的记录吧。
确定做法是对的,有同学用该做法过了所有大样例。
称激活守卫为打开开关。
首先考虑,如果确定所有开关的情况,Bob 有一个简单的贪心做法:当走到一个点时,递归其左右子树并得到两个序列,若右子树的对应序列的小于左子树的对应序列,则右边更优,若开关未被打开,他就会往右走;否则往左走。
考虑能否记录一些简单的东西供 A 参考。
首先,记录剩余魔力值一定是不可能的,这启发我们记录某个决策需要花费的最小魔力值,若剩余魔力值大于它,则该决策可行。
那么我们需要比较两棵子树构成的序列,并据此给出代价。
注意到叶节点上是排列,因此只需记录序列的第一位即可比较大小。因此有自然的想法:在节点 上记录很多 ,表示:站在 上遍历其子树,若 的所有节点都不可达,且 可达,需要花费的最小代价。
求出 后,每次贪心选择让 最大的方案并往下走。假设 在左子树,那么,先把魔力值全部供左子树使用,再将多余的魔力值给右子树使用。为了保证左右子树独立,我们可以再添加一个数 ,表示,为了让 处 的节点不可达,异于 一侧需要花费的最小代价。容易发现扣除 后向下递归,将多余的魔力值与 一起传进另一侧的子树,最优,且一定合法。
显然,魔力值越大,就可以让一棵子树的字典序越大,这里允许不用完的情况。
最优性:首先此时 最大。因为异于 一侧花费的魔力值最小, 所在子树能获得最多的魔力值,即字典序一定最大。
合法性:另一侧的子树可达的权值最小点一定大于 ,字典序增大后仍然成立。
所有 加起来不超过 个,如果我们可以在获得 的左右儿子的 的情况下, 求出 的所有 ,则问题被解决。
考虑按 做归并排序。
假设当前左侧扫到 ,右侧扫到 。设左侧小于右侧,则 ;否则 ,当 不存在时视为正无穷。 可以简单讨论得出,需要同时记录是否开启 。我实现时 代表开启 ,因为显然此时不需要为右边预留魔力值。
有一个细节是,如果剩余的魔力值足够右侧子树的最小值大于当前 ,则可以不开启 ,尽管最小化总花费时需要开启 。
综上,这道题做完了。
#include <bits/stdc++.h>
using LL = long long;
const LL inf = __LONG_LONG_MAX__ / 2;
struct tp {
LL d; int v; LL p;
tp(LL d, int v, LL p) : d(d), v(v), p(p) {}
bool operator < (const tp &t) const {
return d == t.d ? v < t.v : d < t.d;
}
} ;
void solve() {
int n; LL K; scanf("%d %lld", &n, &K);
int siz = (1 << (n + 1)) + 1;
std::vector<LL> w(1 << n);
std::vector<int> v(siz);
// std::vector<LL> prz(1 << (n + 1));
std::vector<std::vector<int>> son(siz);
for (int i = 1; i < (1 << n); i++) scanf("%lld", &w[i]);
for (int i = (1 << n); i < (1 << (n + 1)); i++) scanf("%d", &v[i]);
std::vector<std::vector<tp>> a(siz);
std::function<void(int)> dfs1 = [&](int u) {
if (u >= (1 << n)) {
son[u].push_back(v[u]);
a[u].emplace_back(0, v[u], 0);
return ;
}
int ls = u << 1, rs = u << 1 | 1;
dfs1(ls), dfs1(rs);
for (auto x : son[ls]) son[u].push_back(x);
for (auto x : son[rs]) son[u].push_back(x);
std::sort(son[u].begin(), son[u].end());
int lsiz = a[ls].size(), rsiz = a[rs].size();
int l = 0, r = 0;
LL ml = 0, mr = 0;
while (l != lsiz || r != rsiz) {
LL p = 0;
bool f = 1;
if (r == rsiz || (l != lsiz && a[ls][l].v < a[rs][r].v)) {
if ((r < rsiz ? a[rs][r].d : inf) > w[u]) {
p = -1;
} else {
p = a[rs][r].d;
}
LL d = a[ls][l].d + std::min((r < rsiz ? a[rs][r].d : inf), w[u]);
a[u].emplace_back(d, a[ls][l].v, p);
if (p == -1) assert(d >= w[u]);
++l;
} else {
if (l < lsiz) {
p = (l < lsiz ? a[ls][l].d : inf);
a[u].emplace_back(p + a[rs][r].d, a[rs][r].v, p);
}
++r;
}
}
std::vector<tp> b;
for (int i = a[u].size() - 1; i >= 0; i--) {
if (!b.size() || a[u][i].d <= b.back().d)
b.push_back(a[u][i]);
}
std::reverse(b.begin(), b.end());
a[u] = b;
for (int i = 0; i < a[u].size() - 1; i++)
assert(a[u][i].d <= a[u][i + 1].d);
// printf("\n");
// printf("%d : \n", u);
// for (const auto &[d, v, p] : a[u])
// printf("%lld %d %lld\n", d, v, p);
// printf("\n");
} ;
dfs1(1);
std::vector<int> path;
auto find = [&](int u, LL k) {
return (--std::upper_bound(a[u].begin(), a[u].end(), tp(k, int(1e9), int(1e9))));
} ;
std::function<LL(int, LL)> dfs2 = [&](int u, LL k) {
// printf("dfs %d %lld\n", u, k);
if (u >= (1 << n)) {
path.push_back(v[u]);
return k;
}
auto it = find(u, k);
int v = it->v;
LL p = it->p;
// printf("%d -> %d %lld %lld %lld\n", u, v, it->d, k, it->p);
int ls = u << 1, rs = u << 1 | 1;
auto iter = std::lower_bound(son[ls].begin(), son[ls].end(), v);
if (iter != son[ls].end() && *iter == v) {
// printf("l\n");
if (p == -1) {
LL res = dfs2(ls, k - w[u]);
if (find(rs, res + w[u])->v >= v) res += w[u];
return dfs2(rs, res);
} else {
LL res = dfs2(ls, k - p);
return dfs2(rs, res + p);
}
} else {
// printf("r\n");
LL res = dfs2(rs, k - p);
return dfs2(ls, res + p);
}
} ;
dfs2(1, K);
for (int i = 0; i < (1 << n); i++) printf("%d ", path[i]);
printf("\n");
}
int main() {
int T; scanf("%d", &T); while (T--) {
solve();
// printf("=-------\n");
}
}
作者:purplevine
出处:https://www.cnblogs.com/purplevine/p/18051617
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/18051617
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
2023-03-04 [数学记录]arc154F Dice Game