This commit is contained in:
Zengtudor 2024-11-16 09:08:29 +08:00
parent 43fa88a4c1
commit 7bb2ca422f
8 changed files with 15526 additions and 0 deletions

View File

@ -1,5 +1,50 @@
#include <cstdint>
#include <iostream>
#include <istream>
#include <set>
#include <vector>
using ll = int64_t;
using std::cin, std::cout;
const ll max_n = 1e5+5, rt{1};
ll n, q, c[max_n], fts[max_n];
std::vector<ll> edges[max_n];
void fd_ft(const ll &ft, const ll &now){
fts[now] = ft;
for(const ll &nxt: edges[now]){
if(nxt==ft)continue;
fd_ft(now, nxt);
}
}
int main(){ int main(){
std::iostream::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin>>n>>q;
for(ll i{1};i<=n;i++){
cin>>c[i];
}
for(ll i{1};i<n;i++){
ll u, v;
cin>>u>>v;
edges[u].push_back(v);
edges[v].push_back(u);
}
fd_ft(0, 1);
for(ll i{1};i<=q;i++){
ll u, v;
cin>>u>>v;
std::set<ll> pts;
pts.emplace(c[u]);
while(v!=u){
pts.emplace(c[v]);
v=fts[v];
}
cout<<pts.size()<<'\n';
}
} }

148
src/20241116/U498412.md Normal file
View File

@ -0,0 +1,148 @@
这个题目要求我们对树的路径进行查询,统计某一路径上不同贝壳种类的数量。每个查询给定一个树上两个节点 \( 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 <iostream>
#include <vector>
#include <unordered_map>
#include <set>
using namespace std;
const int MAXN = 100000;
vector<int> tree[MAXN];
int c[MAXN]; // 贝壳种类
int depth[MAXN];
int parent[MAXN];
int n, q;
unordered_map<int, int> path_shelld_count; // 存储路径上贝壳种类的出现次数
vector<pair<int, int>> 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<int> 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 \) 的路径,统计路径上不同贝壳种类的数量。

View File

@ -0,0 +1,10 @@
2
1
2
2
3
3
3
2
2
1

View File

@ -0,0 +1,21 @@
10 10
2 1 2 3 1 1 3 1 3 1
1 2
3 1
4 1
5 4
6 4
7 3
1 8
9 1
5 10
1 2
1 3
3 7
4 5
1 5
1 10
1 6
1 8
4 10
6 6

View File

@ -0,0 +1,100 @@
1
1
1
1
2
1
2
2
1
1
3
1
1
2
1
2
2
1
4
1
1
2
1
1
1
2
2
1
2
1
1
2
1
1
1
3
2
1
1
1
1
1
2
2
2
3
1
3
3
2
1
1
2
3
1
1
1
3
2
1
1
1
1
1
2
1
3
1
2
1
2
1
3
2
2
1
2
3
3
1
2
1
4
2
4
2
1
1
1
4
4
3
3
1
3
2
2
2
3
1

View File

@ -0,0 +1,201 @@
100 100
1 1 6 3 9 1 4 5 1 7 1 1 3 1 1 6 5 1 5 9 5 5 7 6 7 1 4 3 9 1 9 6 1 4 9 7 4 2 10 1 5 6 1 2 1 1 1 1 5 7 6 8 1 6 9 2 7 10 1 1 5 5 8 1 4 2 4 6 9 9 5 9 9 7 5 1 1 5 9 7 3 7 5 1 2 1 6 7 6 6 1 9 1 1 9 1 10 9 7 1
1 2
1 3
3 4
1 5
6 1
1 7
3 8
9 1
10 1
1 11
12 1
13 7
14 11
1 15
1 16
17 1
18 1
1 19
1 20
6 21
22 1
23 19
24 1
12 25
26 1
27 3
28 10
3 29
1 30
31 2
32 11
33 2
34 1
19 35
36 25
37 28
35 38
1 39
3 40
41 33
42 1
17 43
16 44
1 45
46 7
31 47
24 48
29 49
27 50
18 51
50 52
1 53
54 1
55 7
33 56
1 57
58 11
31 59
44 60
16 61
40 62
7 63
30 64
65 1
61 66
67 1
25 68
69 65
59 70
10 71
72 27
64 73
37 74
71 75
42 76
77 17
78 1
69 79
80 1
81 71
82 19
73 83
84 17
57 85
86 32
1 87
88 46
57 89
90 80
6 91
70 92
93 1
26 94
95 43
96 1
97 74
67 98
99 47
100 5
1 1
1 14
1 1
1 1
1 67
1 1
1 70
1 80
1 1
1 1
1 28
1 1
1 1
1 57
43 43
1 76
1 19
1 96
1 37
1 1
87 87
1 47
65 65
1 1
1 1
1 76
1 48
1 1
1 58
1 6
1 12
1 19
1 1
1 1
87 87
1 55
1 7
1 1
1 1
1 1
1 1
1 1
1 67
2 31
1 46
1 35
1 1
1 61
1 27
1 80
1 26
1 1
1 67
1 82
1 1
16 16
1 1
1 4
1 16
78 78
1 93
1 1
1 15
1 93
1 16
1 1
1 23
1 30
1 41
1 1
1 73
1 1
1 61
1 70
1 80
1 1
1 19
1 89
1 75
1 1
1 43
1 1
1 81
1 65
1 66
1 24
1 1
1 1
1 9
1 50
1 37
1 28
1 82
1 1
1 28
1 92
1 84
1 43
31 99
1 1

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long