首页 > 其他分享 >P10553 [ICPC2024 Xi'an I] Guess The Tree 题解

P10553 [ICPC2024 Xi'an I] Guess The Tree 题解

时间:2024-06-08 09:44:43浏览次数:15  
标签:Guess Xi auto int 题解 lca text mp res

挺有意思的题。

思路

考虑一个比较自然的做法。

我们每次对于一棵树,我们将它的某一条链抽出来。

这样,我们只需要知道这颗树的所有节点与链底的 \(\text{lca}\),就可以知道它是属于这条链上哪一个节点的下面。

然后就可以递归处理。

由于交互库不是自适应的。

我们可能可以想到随机一个点。

求出这个点与整颗树的 \(\text{lca}\)。

求出 \(\text{lca}\) 之后,我们一定可以知道这个节点一直到根的链是哪些点,以及他们的父子关系,这个有很多求法,我采用的是按子树大小排序。

这样,我们就已经得到了一个比较优秀的做法。

精细实现后可以做到 \(4700\sim 5000\) 次操作。

但是这个还过不了,因为它的操作次数过于随机,经常会变成 \(4900\sim 5000\)。

我们需要更加好的做法。

考虑什么时候对树的划分最为完全。

一定是一个根到叶子的链可以达到最优的状态。

那么我们不妨将开始随机的点往下跳几步。

具体的:

  • 开始随机一个点 \(x\)。
  • 对于现在在的点 \(x\),我们遍历到了 \(i\)。
  • 求 \(\text{lca}(x,i)\)。
  • 若 \(\text{lca}(x,i)=x\),那么让 \(x\rightarrow i\),然后继续遍历。
  • 否则直接继续遍历。

这样遍历完所有节点后,一定到了一个叶子节点。

但是你写完之后,会发现你的操作次数变成了 \(5000\sim 6000\)。

思考一下问题在哪里。

我们在往下跳的过程中使用了过多的冗余操作。

我们想要把这些操作也用上。

考虑:

若 \(x\) 为 \(y\) 的子树内一点,\(z\) 为 \(y\) 的子树外一点,那么 \(\text{lca}(y,z)=\text{lca}(x,z)\)。

所以,我们在不断往下跳的过程中,\(\text{lca}\) 其实是同样可以往下继承的。

这样就可以在恰好 \(siz-1\) 次操作找到叶子,并求出 \(\text{lca}\)(随机没用了)。

然后你的操作次数就变成了定值,这个定值基于你的实现。

\(10\) 层是可以做到 \(4608\) 或 \(4480\)(特判三个点)。

复杂度基于实现。

Code

这份代码是 \(4480\) 的。

/*
  ! 如果没有天赋,那就一直重复
  ! Created: 2024/06/05 09:10:14
*/
#include <bits/stdc++.h>
using namespace std;

#define x first
#define y second
// #define int long long
#define mp(x, y) make_pair(x, y)
#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
inline void JYFILE19();

using i64 = long long;
using PII = pair<int, int>;

bool ST;
const int N = 1100;
const int mod = 998244353;

int n, m, fa[N], sz[N], mp[N][N];
vector<int> to[N];

inline int lca(int x, int y) {
  if (mp[x][y]) return mp[x][y];
  cout << "? " << x << " " << y << endl;
  cin >> mp[x][y];
  return mp[y][x] = mp[x][y];
}
inline void upd(int x, int y, int k) {
  if (mp[x][y]) return;
  mp[x][y] = mp[y][x] = k;
}
inline auto sol(vector<int> all, int las = 0) {
  if (all.size() == 0) return 0;
  if (all.size() == 1) return all[0];
  if (all.size() == 2) return fa[all[0]] = las, all[1];
  if (all.size() == 3) {
    int x = lca(all[0], all[1]);
    for (auto i : all) if (i != x) fa[i] = x;
    return x;
  }
  int x = all[0]; vector<int> res;
  for (auto i : all) {
    if (lca(i, x) == x) {
      for (auto j : all) {
        if (i == j) break;
        upd(i, j, lca(x, j));
      }
      x = i;
    }
  }
  for (auto i : all) {
    sz[lca(i, x)]++;
    res.eb(lca(i, x));
  }
  sort(res.begin(), res.end(), [&](int x, int y) {
    return sz[x] < sz[y];
  });
  res.erase(unique(res.begin(), res.end()), res.end());
  for (int i = 0; i < res.size() - 1; i++)
    fa[res[i]] = res[i + 1];
  for (auto i : all) {
    for (auto j : res) if (i == j) goto ed;
    if (lca(i, x) != i) to[lca(i, x)].eb(i);
    ed:;
  }
  for (auto i : res) {
    auto nt = to[i];
    to[i].clear();
    auto x = sol(nt, i);
    if (x != i) fa[x] = i;
  }
  return res.back();
}

signed main() {
  JYFILE19();
  cin >> n, n = (1 << n) - 1;
  vector<int> all;
  fill(fa + 1, fa + n + 1, -1);
  fro(i, 1, n) all.eb(i), mp[i][i] = i;
  sol(all);
  cout << "! ";
  fro(i, 1, n) cout << fa[i] << " ";
  cout << endl;
  return 0;
}

bool ED;
inline void JYFILE19() {
  // freopen("", "r", stdin);
  // freopen("", "w", stdout);
  srand(random_device{}());
  ios::sync_with_stdio(0), cin.tie(0);
  double MIB = fabs((&ED - &ST) / 1048576.), LIM = 32;
  // cerr << "MEMORY: " << MIB << endl, assert(MIB <= LIM);
}

标签:Guess,Xi,auto,int,题解,lca,text,mp,res
From: https://www.cnblogs.com/JiaY19/p/18238312

相关文章

  • P9000 [CEOI2022] Measures 题解
    思路简单题。考虑任意两点之间的限制。任意两点合法时必须要满足:\[\frac{d(j-i)-(a_j-a_i)}{2}\let(i\lej)\]所以答案即为:\[\max_{i\lej}\frac{d(j-i)-(a_j-a_i)}{2}\]使用线段树简单维护即可。时间复杂度:\(O((n+m)\log(n+m))\)。Code#include<bits/stdc++.h>using......
  • CF1192B Dynamic Diameter 题解
    思路静态\(\text{toptree}\)板子题。定义我们使用簇来表示树上的一个连通块。可以按照如下方式定义一个簇:一个簇可以表示为三元组\((u,v,E)\),其中\(u,v\)为树的节点,称为簇的界点,\(E\)为一个边的集合,表示该簇包含的边,路径\((u,v)\)称作簇路径。\(u,v\)分别为上界......
  • 无限之环 题解
    五星压行大师\(lyh\)表示:这是难得能让他的代码长度打破百行大关的题目(182行)。首先,根据科技与狠活,本题可以黑白染色。源点联向白格,黑格连向汇点。发现每个格子都可以连向四个方向,所以可以建立四个点,代表水管连到了上下左右四个方向。设四元组\((x,y,z,p)\)表示水管初始状态......
  • AXI Quad SPI IP核基于AXI-Lite接口的标准SPI设计指南
    在标准SPI配置下,SPI设备除了包含基本的SPI特性外,还具备以下一些标准功能,这些功能如下所示:支持FPGA内部的多主设备配置,其中使用单独的_I(输入)、_O(输出)、_T(三态)表示三态端口。这种配置允许在FPGA内部有多个主设备共享SPI总线,通过三态驱动器来实现。在默认配置下支持N次8位数据字......
  • Pixi.js学习 (四)鼠标跟随、字符拼接与图片位控
    目录目录目录前言一、鼠标移动跟随1.1获取鼠标坐标1.2 鼠标跟随二、锚点、元素组合2.1锚点2.2 元素组合2.3总结前言为了提高作者的代码编辑水品,作者在使用博客的时候使用的集成工具为HBuilderX。下文所有截图使用此集成工具,读者随意。此系列文......
  • 开车旅行|倍增优化dp+双端列表/set|题解
    题面:题面链接这题的思路值得借鉴,也是我做的第一道倍增优化dp题目.比较好的是题目的意思较为清晰,所以不再赘述.解析:这里我们可以非常直接的想到暴力模拟,因为第一眼看上去前七个点的数据范围是支持我们进行一个简单的预处理得到对应人在对应位置的决策的.(排序O(n×sqrt(......
  • [ABC107D] Median of Medians 题解
    题目大意:一个长度为$M$的序列的中位数为这个序列从小到大排序后第$\lfloor\fracM2\rfloor+1$个数,将这个序列的所有子段的中位数放入一个序列中,求这个序列的中位数。设一个序列$a$的中位数为$x$,那么$a$中至少会有一半的数大于等于$x$,并且$x$是$a$中满足这个条件......
  • プログラミングコンテストチャレンジブック 题解
    题目大意:从$n$根木棒里选出六根拼成两个合法的三角形,使这两个三角形的周长和最大。考虑贪心,证明在后面。首先我们要知道一个三角形基本定理:较短两边长度之和大于最长边。那我们就根据这个定理先去寻找最大三角形的最长边。先排序,然后循环枚举,对于每个$a_i$,可以寻找到的最大......
  • [COCI2020-2021#2] Sjekira 题解
    题目大意:把一棵树完全分解,每次分解一条边的代价是这条边连接的两个连通块的最大点权之和,求最小代价。逆序模拟,既然题目要求将树完全分解,那我们就每次逆序连接当前权值最小的两个点,也就是贪心的思路。尝试将贪心的值写成一个表达式:$$\sum_{i=1}^na_i+\sum_{(u,v)\inE}\max(a......
  • [ABC036D] 塗り絵 题解
    题意题面讲挺清楚的就不简化了。思路树上求方案数,很明显是树上dp。设$dp_{i,0/1}$表示第$i$个点涂成白/黑色的方案数。当前结点如果为白色,那么它的子节点涂成什么颜色都没关系,根据分步乘法原理,将它子结点涂成白/黑色的方案数之和乘起来即可;当前结点如果为黑色,那么它的子......