1 条题解
-
0
题目分析
问题概述
我们有一个包含 个行星的树结构,地球编号为 。需要从地球出发访问 个起源行星,访问路径上的所有行星构成集合 。题目要求处理两种查询:
- 查询集合 的大小
- 对于给定的 ,求距离 为 的所有行星中按人口数排序的第 小的行星
关键观察
- 树结构性质: 条边连接 个节点,构成一棵树
- 集合 的构成: 是从地球到所有起源行星的简单路径的并集
- 距离计算:在树中,两点距离 = 深度[u] + 深度[v] - 2 × 深度[LCA(u,v)]
解法思路
方法:LCA预处理 + 路径标记 + 距离分类
步骤1:预处理LCA信息
使用DFS和二进制提升法预处理每个节点的父节点信息,用于快速计算LCA和距离。
步骤2:确定集合S
从地球出发,标记到每个起源行星路径上的所有节点:
- 使用LCA找到路径
- 标记路径上的所有节点
步骤3:处理查询
- 类型1:直接输出集合S的大小
- 类型2:对于每个查询,需要高效找到距离x为d的所有行星并按人口排序
高效实现
核心算法
#include <bits/stdc++.h> using namespace std; const int MAXN = 100005; const int LOG = 17; int n, k, q; vector<int> adj[MAXN]; int population[MAXN]; vector<int> origins; bool inS[MAXN]; int szS = 0; // LCA相关 int depth[MAXN]; int parent[MAXN][LOG]; void dfs(int u, int p, int d) { depth[u] = d; parent[u][0] = p; for (int i = 1; i < LOG; i++) { if (parent[u][i-1] != -1) { parent[u][i] = parent[parent[u][i-1]][i-1]; } } for (int v : adj[u]) { if (v != p) { dfs(v, u, d + 1); } } } int lca(int u, int v) { if (depth[u] < depth[v]) swap(u, v); // u提升到与v同一深度 for (int i = LOG - 1; i >= 0; i--) { if (depth[u] - (1 << i) >= depth[v]) { u = parent[u][i]; } } if (u == v) return u; // 同时提升 for (int i = LOG - 1; i >= 0; i--) { if (parent[u][i] != parent[v][i]) { u = parent[u][i]; v = parent[v][i]; } } return parent[u][0]; } int distance(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; } void markPath(int u, int v) { int l = lca(u, v); // 标记u到lca的路径 int cur = u; while (cur != l) { if (!inS[cur]) { inS[cur] = true; szS++; } cur = parent[cur][0]; } // 标记v到lca的路径 cur = v; while (cur != l) { if (!inS[cur]) { inS[cur] = true; szS++; } cur = parent[cur][0]; } // 标记lca if (!inS[l]) { inS[l] = true; szS++; } }查询处理优化
对于类型2查询,直接计算会超时,需要预处理:
// 为每个可能的距离预先计算行星列表 vector<vector<int>> planetsByDist[MAXN]; void precomputeDistances() { for (int i = 0; i <= n; i++) { planetsByDist[i].resize(MAXN); } // 对每对节点计算距离并分类 for (int i = 0; i <= n; i++) { for (int j = i; j <= n; j++) { int dist = distance(i, j); if (dist < MAXN) { planetsByDist[i][dist].push_back(j); if (i != j) { planetsByDist[j][dist].push_back(i); } } } } // 对每个距离列表按人口排序 for (int i = 0; i <= n; i++) { for (int dist = 0; dist < MAXN; dist++) { if (!planetsByDist[i][dist].empty()) { sort(planetsByDist[i][dist].begin(), planetsByDist[i][dist].end(), [&](int a, int b) { return population[a] < population[b]; }); } } } }主程序
int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> n >> k >> q; // 读入数据 for (int i = 0; i <= n; i++) { cin >> population[i]; } origins.resize(k); for (int i = 0; i < k; i++) { cin >> origins[i]; } for (int i = 0; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } // 初始化 memset(parent, -1, sizeof(parent)); // 预处理LCA dfs(n, -1, 0); // 标记集合S inS[n] = true; szS = 1; for (int origin : origins) { markPath(n, origin); } // 预处理距离信息 precomputeDistances(); // 处理查询 while (q--) { int type; cin >> type; if (type == 1) { cout << szS << "\n"; } else { int x, d, r; cin >> x >> d >> r; cout << planetsByDist[x][d][r - 1] << "\n"; } } return 0; }空间优化版本
对于大数据,上述方法空间开销太大。可以采用按需计算的策略:
// 按需计算距离查询 int queryType2(int x, int d, int r) { vector<int> candidates; // 使用BFS收集距离x为d的所有节点 queue<pair<int, int>> q; vector<bool> visited(n + 1, false); q.push({x, 0}); visited[x] = true; while (!q.empty()) { auto [node, dist] = q.front(); q.pop(); if (dist == d) { candidates.push_back(node); continue; } if (dist > d) continue; for (int neighbor : adj[node]) { if (!visited[neighbor]) { visited[neighbor] = true; q.push({neighbor, dist + 1}); } } } // 按人口排序 sort(candidates.begin(), candidates.end(), [&](int a, int b) { return population[a] < population[b]; }); return candidates[r - 1]; }复杂度分析
方法1:完全预处理
- 时间复杂度:
- 预处理:(距离计算)
- 查询:
- 空间复杂度:
- 适用性:适合 的情况
方法2:按需计算
- 时间复杂度:
- 预处理:(仅LCA)
- 查询:
- 空间复杂度:
- 适用性:适合大数据,但查询较慢
方法3:平衡方案
对于 ,可以:
- 预处理所有节点到地球的距离
- 对于查询,利用树的性质和LCA快速计算距离
- 使用哈希表缓存常见查询
算法总结
- LCA预处理:使用二进制提升法,支持 的LCA查询
- 路径标记:通过LCA找到路径并标记访问节点
- 距离查询优化:
- 小数据:完全预处理
- 大数据:按需BFS + 排序
- 排序策略:按人口数排序,使用自定义比较函数
关键技巧
- 二进制提升:高效计算LCA
- 路径分解:将路径分解为u→LCA和v→LCA两部分
- 距离性质:在树中,距离满足三角不等式
- 查询优化:根据数据规模选择合适的预处理策略
这道题综合考察了树的基本操作、LCA算法、路径处理和查询优化,是一道很好的综合性树问题。
- 1
信息
- ID
- 5436
- 时间
- 5000ms
- 内存
- 1024MiB
- 难度
- 9
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者