首页 > 其他分享 >[CEOI2023] The Ties That Guide Us 题解

[CEOI2023] The Ties That Guide Us 题解

时间:2024-11-15 19:09:04浏览次数:1  
标签:sz cur int 题解 房间 保险箱 CEOI2023 Ties mx

Description

你用销售机器人的利润雇佣了一名助手,现在你准备好去拿走装有 CEOI 奖章的保险箱了。

保险箱位于一所由 \(n\) 个房间所组成的大学建筑内,这些房间由 \(n-1\) 扇门连接。每个房间都可以从其他任何房间到达,且每个房间最多与 \(3\) 扇门相连。

你和你的助手都有描述建筑物内房间相连情况的平面图,但是你们两个各自拥有的平面图虽然描述了相同的房间结构布局,但是房间和门的编号可能不同。

在比赛的第二天,委员会忙于处理赛时通知和选手提问。这将是接近装着奖牌的保险箱的完美机会。

你的助手会首先搜索整栋大楼。一旦他找到保险箱所在的房间,它就会给你留下前往那个房间的提示。由于手机不能带进赛场,他用了去年 BOI 留下的几乎无限供应的领带。由于这些领带完全相同无法区分,你能获得的信息就是他在任何给定房间里所留下的领带数量。由于一个房间内过多的领带非常可疑,因此任何单个房间内领带的最大数量应当尽可能少(参阅评分部分)。

之后,你计划在上厕所的时候溜出去,利用助手留下来的领带找到有保险箱的房间。保险箱藏在房间里,所以你进入带有保险箱的房间时,必须依靠领带识别这个房间;此外,由于“上厕所”时间过长会被发现,你必须尽快找到保险箱。你最多可以走过 \(d+30\) 扇门,其中 \(d\) 是你的初始位置到保险箱所在位置的最短路径上的门数量。若重复穿过同一扇门,则每次都计入。

因此,你需要编写一个程序,告诉助手需要在每个房间留下多少条领带,并引导你前往带有保险箱的房间。

Solution

首先对于 Sub2 只有一个点度数为 \(2\),所以可以直接取出这个唯一的点作为根,然后将目标点到根的路径颜色染黑。这样查询的时候只要暴力跳父亲,直到当前点颜色为黑,然后暴力枚举儿子,如果找到颜色为黑的儿子就往儿子跳,否则就是终点了。

不过这么做有个问题,就是找儿子的过程可能会浪费过多的询问次数,而注意到限制是 \(dist(s,t)+30\),由于每次试错会浪费两次,所以只能走错 \(15\) 步,为 \(O(\log n)\) 级别。

这启发我们重链剖分,每次跳子树大小最大的儿子,由于终点到 \(lca\) 路径上的轻儿子只有 \(O(\log n)\) 个,所以这样就是对的。

如果树没有特殊性质,可以利用树哈希的思想选定重心作为根,但是可能有两个重心,这时需要选取离起点更近的重心作为根。

查询时如果跳到根节点颜色还为白,就说明根是另一个重心,直接跳到那个重心即可。

总次数:\(d+2\log n\)。

Code

#include <bits/stdc++.h>
#include "incursion.h"

#ifdef ORZXKR
#include "grader.cpp"
#endif

const int kMaxN = 4.5e4 + 5;

int n, safe;
int p[kMaxN], sz[kMaxN], mx[kMaxN];
bool vis[kMaxN];
std::vector<int> G[kMaxN], rt;

void dfs1(int u, int fa) {
  sz[u] = 1, mx[u] = 0;
  for (auto v : G[u]) {
    if (v == fa) continue;
    dfs1(v, u);
    sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);
  }
  mx[u] = std::max(mx[u], n - sz[u]);
  if (mx[u] <= n / 2) rt.emplace_back(u);
}

void dfs2(int u, int fa) {
  p[u] = fa;
  for (auto v : G[u]) {
    if (v == fa) continue;
    dfs2(v, u);
  }
}

void dfs3(int u, int fa) {
  vis[u] = 1;
  for (auto v : G[u]) {
    if (v == fa || vis[v]) continue;
    if (visit(v)) return dfs3(v, u);
    else visit(u);
  }
}

std::vector<int> mark(std::vector<std::pair<int, int>> F, int safe) {
  n = (int)F.size() + 1;
  std::vector<int> vec(n, 0);
  for (int i = 1; i <= n; ++i) G[i].clear();
  for (auto [u, v] : F)
    G[u].emplace_back(v), G[v].emplace_back(u);
  rt.clear(), dfs1(1, 0), dfs2(rt[0], 0);
  for (int i = safe; i; i = p[i]) {
    vec[i - 1] = 1;
    if (rt.size() == 2 && (i == rt[0] || i == rt[1])) break;
  }
  return vec;
}

void locate(std::vector<std::pair<int, int>> F, int cur, int t) {
  n = (int)F.size() + 1;
  for (int i = 1; i <= n; ++i) G[i].clear(), vis[i] = 0;
  for (auto [u, v] : F)
    G[u].emplace_back(v), G[v].emplace_back(u);
  rt.clear(), dfs1(1, 0), dfs2(rt[0], 0);
  for (int i = 1; i <= n; ++i)
    std::sort(G[i].begin(), G[i].end(), [&] (int x, int y) { return sz[x] > sz[y]; });
  for (; p[cur] && !t; t = visit(cur = p[cur])) vis[cur] = 1;
  vis[cur] = 1;
  if (!t) assert(cur == rt[0]), visit(cur = rt[1]);
  vis[cur] = 1;
  dfs3(cur, p[cur]);
}

标签:sz,cur,int,题解,房间,保险箱,CEOI2023,Ties,mx
From: https://www.cnblogs.com/Scarab/p/18548514

相关文章

  • P1437 敲砖块 题解
    题意在一个凹槽中放置了\(n\)层砖块、最上面的一层有\(n\)块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示:14154323333376221311222331如果你想敲掉第\(i\)层的第\(j\)块砖的话,若\(i=1\),你可以直接......
  • [CEOI2023] Tricks of the Trade 题解
    Description有\(n\)个机器人排成一排,第\(i\)个机器人的购买价是\(a_i\)欧元,卖出价是\(b_i\)欧元。给定\(1\lek\len\),你需要购买一段长度至少为\(k\)的区间中所有的机器人,然后选择其中的恰好\(k\)个机器人来卖出。你需要求出:你能够得到的最大收益;在收益最大化......
  • P11276 第一首歌 题解
    P11276第一首歌题解一道简单的字符串题目。题目解释说求一个最短的字符串\(t\),使其满足对于给定的字符串\(s\):\(s\)为\(t\)的前缀。\(s\)为\(t\)的后缀。\(s\)不为\(t\)。仔细思考一下,则易得\(t\)的最短长度的最大值为\(s\)的两倍。即当\(s\)......
  • 读数据质量管理:数据可靠性与数据质量问题解决之道04收集与清洗
    1.      收集数据1.1.        数据收集和清洗是生产管道中的第一步1.1.1.          数据转换和测试则在生产管道中解决数据质量问题1.2.        在收集数据时,管道的任何地方可能都没有入口点重要,因为入口点是任何数据管道中最上游的位......
  • 题解:P11277 世界沉睡童话
    比较简单的构造。注意到题面给出\(a_i\le2n-1\)的条件,考虑这个有什么用,你会发现从\(n\)到\(2n-1\)这\(n\)个数都是两两互不为约数,所以当我们构造出序列后,这些数可以用来填补空位。\(k\)的上界是\(\frac{n(n-1)}{2}\),显然在全部都为同一个数的时候取到,显然有\(x\)个......
  • Atcoder ABC 216 G 01Sequence 题解 [ 蓝 ] [ 差分约束 ]
    01Sequence:比较板的差分约束,但有一个很妙的转化。朴素差分约束设\(x_i\)表示第\(i\)位的前缀和。我们要最小化\(1\)的个数,就要求最小解,就要求最长路。因为约束条件都是大于等于号,所以求最长路才能满足所有条件。求最大解也是同理。我们可以对于每一个条件,列出如下不等式......
  • 2024年09月CCF-GESP编程能力等级认证Python编程二级真题解析
    本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。一、单选题(每题2分,共30分)第1题据有关资料,山东大学于1972年研制成功DJL-1计算机,并于1973年投入运行,其综合性能居当时全国第三位。DJL-1计算机运算控制部分所使用的......
  • [CF1188E] Problem from Red Panda 题解
    [CF1188E]ProblemfromRedPanda题解考虑每个位置的操作次数\(c_i\),不难发现,\(i\)气球最后的颜色个数\(d_i\)是\(a_i+c_ik-\sumc_i\),如果存在\(\forallc_i>0\),那么我们总是可以把所有气球少操作一次,这样上式不变,不影响最后的序列,下文所有的操作序列都假设\(\min......
  • [JXOI2017] 加法 题解
    [JXOI2017]加法最小值最大,一眼二分。贪心地,每次尽量对包含当前序列最小值的区间做加法操作,也就是说,对于当前二分的答案\(x\),任何的\(A_i<x\)都需要被操作。从左到右地考虑答案。我们认为当前点之前的所有值都已经满足条件,于是我们只需考虑每次区间对当前点之后答案造成的贡......
  • 题解:P7836 「Wdoi-3」夜雀 collecting
    题解摘自做题记录。分析数据范围明显得要让我们分开搞。【Sub2】应该是暴力。这里有个主体思路,我们完全可以贪心地将当前背包里的食材删掉,直到每种出现过的食材数量刚好为\(1\)。因为我们保留更多的是没有用的。那么我们就可以用二进制数表示\(x\)种食材的出现状态了。同......