diff --git a/src/20241116/U498412.cpp b/src/20241116/U498412.cpp index bcd8ea0..6e9cdc5 100644 --- a/src/20241116/U498412.cpp +++ b/src/20241116/U498412.cpp @@ -20,8 +20,6 @@ void fd_ft(const ll &ft, const ll &now){ } } - - int main(){ std::iostream::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); diff --git a/src/20241116/U498412.md b/src/20241116/U498412.md index 8268054..e69de29 100644 --- a/src/20241116/U498412.md +++ b/src/20241116/U498412.md @@ -1,148 +0,0 @@ -这个题目要求我们对树的路径进行查询,统计某一路径上不同贝壳种类的数量。每个查询给定一个树上两个节点 \( u \) 和 \( v \),我们需要计算从 \( u \) 到 \( v \) 路径上有多少种不同的贝壳种类。 - -### 思路 - -这个问题的核心在于计算路径上的不同元素个数。首先,这是一棵树,树上没有环,因此从任意一个节点到另一个节点的路径是唯一的。我们可以利用树的结构,结合一些优化技巧来高效地解决这个问题。 - -#### 1. 树的路径表示 -从 \( u \) 到 \( v \) 的路径是树上的一条简单路径。我们可以使用 **树的祖先关系** 来表示路径。具体来说,任意一条从节点 \( u \) 到节点 \( v \) 的路径都可以分解为以下两部分: -- 从 \( u \) 到树的根节点(节点 1)的路径。 -- 从 \( v \) 到树的根节点的路径。 - -通过找到两个路径的公共祖先 \( LCA(u, v) \)(最近公共祖先),我们就可以通过路径合并来得到从 \( u \) 到 \( v \) 的完整路径。 - -#### 2. 离线查询和路径压缩 -为了高效地回答每个查询,我们可以考虑 **离线查询** 和 **路径压缩** 的结合。离线查询的意思是先对所有查询进行排序,然后逐个处理。由于路径查询操作是基于树的结构,我们可以借助 **DFS** 预处理树的深度信息和树的结构。 - -#### 3. 树的深度和父节点预处理 -使用 **深度优先搜索(DFS)** 遍历树,可以构建每个节点的深度、父节点等信息。这些信息将有助于我们高效地计算最近公共祖先(LCA)。 - -#### 4. 使用哈希表计算不同贝壳种类 -在查询路径时,我们可以维护一个哈希表来存储路径上出现的贝壳种类。每次遇到新节点时,将其贝壳种类加入哈希表并进行更新。 - -### 具体步骤 - -1. **DFS遍历树:** - - 通过DFS计算每个节点的深度。 - - 使用 **Euler Tour**(欧拉遍历)来记录节点在树上的访问顺序,便于快速查询LCA。 - -2. **离线查询:** - - 将所有查询按某种方式排序(例如按深度或时间戳排序),并结合DFS结果处理每个查询。 - -3. **LCA计算:** - - 可以使用 **二分法提升(Binary Lifting)** 来优化LCA查询,预处理过程中记录每个节点的2^i父节点。 - -4. **路径上贝壳种类的计算:** - - 在查询时,通过路径压缩技巧,实时更新路径上的贝壳种类。 - -5. **维护结果:** - - 对每个查询返回路径上不同贝壳种类的数量。 - -### C++ 代码实现 - -```cpp -#include -#include -#include -#include -using namespace std; - -const int MAXN = 100000; -vector tree[MAXN]; -int c[MAXN]; // 贝壳种类 -int depth[MAXN]; -int parent[MAXN]; -int n, q; - -unordered_map path_shelld_count; // 存储路径上贝壳种类的出现次数 -vector> queries[MAXN]; // 存储每个节点的查询 - -// dfs遍历,记录每个节点的深度和父节点 -void dfs(int u, int par) { - parent[u] = par; - for (int v : tree[u]) { - if (v != par) { - depth[v] = depth[u] + 1; - dfs(v, u); - } - } -} - -// 找LCA -int LCA(int u, int v) { - if (depth[u] < depth[v]) swap(u, v); - // 把u提升到和v一样深 - while (depth[u] > depth[v]) u = parent[u]; - // 同时提升u和v直到它们相等 - while (u != v) { - u = parent[u]; - v = parent[v]; - } - return u; -} - -void process_query(int u, int v) { - set unique_shells; // 用set记录贝壳种类 - - // 找路径u->root - while (u != -1) { - unique_shells.insert(c[u]); - u = parent[u]; - } - - // 找路径v->root - while (v != -1) { - unique_shells.insert(c[v]); - v = parent[v]; - } - - // 返回路径上不同贝壳种类的数量 - cout << unique_shells.size() << endl; -} - -int main() { - cin >> n >> q; - for (int i = 0; i < n; i++) cin >> c[i]; // 贝壳种类 - for (int i = 0; i < n-1; i++) { - int u, v; - cin >> u >> v; - u--; v--; - tree[u].push_back(v); - tree[v].push_back(u); - } - - // 处理树 - depth[0] = 0; - dfs(0, -1); - - // 处理查询 - for (int i = 0; i < q; i++) { - int u, v; - cin >> u >> v; - u--; v--; // 转化为0-indexed - process_query(u, v); - } - - return 0; -} -``` - -### 总结 - -1. **DFS遍历**:用来计算每个节点的深度和父节点,便于LCA计算。 -2. **LCA查询**:通过二分提升方法实现,快速找到两节点的最近公共祖先。 -3. **贝壳种类统计**:使用`set`或者`unordered_map`来统计路径上不同的贝壳种类数量。 - -该算法的复杂度主要依赖于树的深度和LCA查询的优化,可以保证在 \( O(n \log n) \) 复杂度下处理完所有查询。 -这句话的意思是,给定一棵以节点 `1` 为根的树,JJ 想知道从节点 `u` 到节点 `v` 的路径上包含了多少种不同的贝壳种类。树上的路径表示从 `u` 开始,沿着树的边走到 `v` 的路径。 - -由于题目说明 **“保证 \( u \) 在 \( 1 \sim v \) 的路径上”**,可以解释为以下两点: -1. 节点 \( u \) 一定是在根节点(节点 `1`)到节点 \( v \) 的路径上的某个节点。 -2. \( u \) 可以等于 \( v \),即路径可以只包含一个节点。 - -这个问题要求的是,在给定的路径上,从节点 \( u \) 开始,经过所有的中间节点到达节点 \( v \),这段路径中包含了多少种不同的贝壳。例如: - -- 如果路径上贝壳种类是 `[2, 1, 2, 3]`,则不同的贝壳种类数是 `3`(种类分别是 `2`, `1`, 和 `3`)。 -- 如果路径上贝壳种类是 `[1, 1, 1]`,则不同的贝壳种类数是 `1`(只有 `1` 一种贝壳)。 - -要回答这个问题,必须遍历 \( u \) 到 \( v \) 的路径,统计路径上不同贝壳种类的数量。 \ No newline at end of file diff --git a/src/20241116/U498417.cpp b/src/20241116/U498417.cpp new file mode 100644 index 0000000..294989d --- /dev/null +++ b/src/20241116/U498417.cpp @@ -0,0 +1,3 @@ +int main(){ + +} \ No newline at end of file