update
This commit is contained in:
parent
97dcb4586e
commit
d276eea07c
@ -1,3 +1,4 @@
|
|||||||
|
#include <cctype>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -7,40 +8,91 @@
|
|||||||
using ll = int64_t;
|
using ll = int64_t;
|
||||||
using std::cin, std::cout;
|
using std::cin, std::cout;
|
||||||
|
|
||||||
const ll maxn = 5e5+5;
|
// 定义最大节点数为5e5加5,以确保足够的空间
|
||||||
ll n, k, a[maxn], ans[maxn];
|
const ll maxn = 5e5 + 5;
|
||||||
std::vector<std::pair<ll, ll>> adj[maxn];
|
ll n, k; // n: 树的节点数,k: 暴龙的种类数
|
||||||
std::map<ll,ll> dp[maxn];//dp[i][j]:第i个节点距离颜色j的最大值
|
ll a[maxn]; // a[i]: 第i个节点的暴龙种类
|
||||||
|
ll ans[maxn]; // ans[i]: 第i种暴龙的红温值
|
||||||
|
std::vector<std::pair<ll, ll>> adj[maxn]; // adj[u]: 节点u的邻居及边权
|
||||||
|
std::map<ll, ll> dp[maxn]; // dp[u][j]: 节点u到子树中种类j的最大距离
|
||||||
|
|
||||||
void dfs(const ll &fth, const ll &u)noexcept{
|
// 优化输入读取,提高IO速度
|
||||||
dp[u].emplace(a[u],0);
|
struct CinNum {
|
||||||
// dp[u][a[u]]=0;
|
char c;
|
||||||
for(const auto& [v,w]:adj[u]){
|
ll n, w;
|
||||||
if(v==fth)continue;
|
// 重载右移运算符以自定义输入方式
|
||||||
dfs(u,v);
|
CinNum &operator>>(ll &num) {
|
||||||
for(const auto& [kk,vv]:dp[v]){
|
c = n = 0;
|
||||||
if((dp[u][kk]>0||a[u]==kk)&&(dp[v][kk]>0||a[v]==kk)){
|
w = 1;
|
||||||
//更新与u,v子树有关的红温值
|
// 跳过非数字字符,记录负号
|
||||||
ans[kk]=std::max(ans[kk], dp[u][kk] + dp[v][kk] + w);
|
while (!isdigit(c = getchar())) {
|
||||||
}
|
if (c == '-') w = -1;
|
||||||
dp[u][kk]=std::max(dp[u][kk],dp[v][kk]+w);
|
|
||||||
if(a[u]==kk)ans[kk]=std::max(ans[kk],dp[u][kk]);//更新当前节点的红温值
|
|
||||||
}
|
}
|
||||||
|
// 读取数字
|
||||||
|
while (isdigit(c)) {
|
||||||
|
n = n * 10 + (c - '0');
|
||||||
|
c = getchar();
|
||||||
|
}
|
||||||
|
num = n * w;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
} cinn;
|
||||||
|
// 使用自定义的CinNum来替代标准输入
|
||||||
|
#define cin cinn
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用深度优先搜索 (DFS) 计算每种暴龙的红温值
|
||||||
|
*
|
||||||
|
* @param fth 当前节点的父节点
|
||||||
|
* @param u 当前处理的节点
|
||||||
|
*/
|
||||||
|
void dfs(const ll &fth, const ll &u) noexcept {
|
||||||
|
// 初始化当前节点的dp,自己到自己距离为0
|
||||||
|
dp[u].emplace(a[u], 0);
|
||||||
|
|
||||||
|
// 遍历所有邻接节点
|
||||||
|
for (const auto& [v, w] : adj[u]) {
|
||||||
|
if (v == fth) continue; // 避免回到父节点
|
||||||
|
dfs(u, v); // 递归处理子节点
|
||||||
|
|
||||||
|
// 遍历子节点v的所有暴龙种类及其对应的最大距离
|
||||||
|
for (const auto& [kk, vv] : dp[v]) {
|
||||||
|
// 如果当前节点u已经有这种暴龙类型,且子节点v也有
|
||||||
|
if ((dp[u][kk] > 0 || a[u] == kk) && (dp[v][kk] > 0 || a[v] == kk)) {
|
||||||
|
// 更新ans[kk]为u和v子树中这种暴龙类型的最大距离
|
||||||
|
ans[kk] = std::max(ans[kk], dp[u][kk] + dp[v][kk] + w);
|
||||||
|
}
|
||||||
|
// 更新u节点到这种暴龙类型的最大距离
|
||||||
|
dp[u][kk] = std::max(dp[u][kk], dp[v][kk] + w);
|
||||||
|
|
||||||
|
// 如果当前节点u本身就是这种暴龙类型,进一步更新ans[kk]
|
||||||
|
if (a[u] == kk) {
|
||||||
|
ans[kk] = std::max(ans[kk], dp[u][kk]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 清空子节点v的dp以节省内存,因为已经合并到u的dp中了
|
||||||
dp[v].clear();
|
dp[v].clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(){
|
int main(){
|
||||||
std::iostream::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
|
// 读取节点数n和暴龙种类数k
|
||||||
|
cin >> n >> k;
|
||||||
|
|
||||||
cin>>n>>k;
|
// 读取每个节点的暴龙种类
|
||||||
for(ll i{1};i<=n;i++)cin>>a[i];
|
for(ll i = 1; i <= n; i++) cin >> a[i];
|
||||||
for(ll i{1};i<n;i++){
|
|
||||||
ll u,v,w;
|
// 读取树的边信息,并构建邻接表
|
||||||
cin>>u>>v>>w;
|
for(ll i = 1; i < n; i++){
|
||||||
adj[u].emplace_back(v,w);
|
ll u, v, w;
|
||||||
adj[v].emplace_back(u,w);
|
cin >> u >> v >> w;
|
||||||
|
adj[u].emplace_back(v, w);
|
||||||
|
adj[v].emplace_back(u, w);
|
||||||
}
|
}
|
||||||
dfs(0,1);
|
|
||||||
for(ll i{1};i<=k;i++)cout<<ans[i]<<'\n';
|
// 从节点1开始DFS遍历整棵树,假设节点1为根节点,父节点为0
|
||||||
|
dfs(0, 1);
|
||||||
|
|
||||||
|
// 输出每种暴龙的红温值
|
||||||
|
for(ll i = 1; i <= k; i++) cout << ans[i] << '\n';
|
||||||
}
|
}
|
@ -1,38 +1,227 @@
|
|||||||
这段代码中的 `if` 语句主要是用于确保在更新暴龙的红温值 (最大距离) 时判断当前的节点类型与子树中的节点类型之间的关系。具体来说,这里是在处理当前节点 `u` 和子节点 `v` 之间的距离关系,同时考虑了他们的暴龙类型。
|
# 暴龙
|
||||||
|
|
||||||
我们来逐步解析这个 `if` 语句及其包含的逻辑:
|
## 题目背景
|
||||||
|
|
||||||
|
Hanghang 红温了,然后他变异成了暴龙。
|
||||||
|
|
||||||
|
## 题目描述
|
||||||
|
|
||||||
|
机房里有 $k$ 种暴龙。现在他们占据了一棵树上的所有的点。这棵树有 $n$ 个点,边有边权。现在对于某一种暴龙,他们的红温值定义为任意两个这种暴龙的距离的最大值。如果这种暴龙的出现次数小于 $2$,则红温值为 $0$。考拉想要证明 Hanghang 红温了,因此他想要问你每种暴龙的红温值。
|
||||||
|
|
||||||
|
## 输入格式
|
||||||
|
|
||||||
|
第一行两个正整数 $n,k$,表示树的节点数和暴龙的种类数。
|
||||||
|
|
||||||
|
第二行 $n$ 个在 $[1,k]$ 间的正整数 $p_i$,表示每个节点的暴龙的种类。
|
||||||
|
|
||||||
|
接下来 $n-1$ 行,每行三个正整数 $a,b,c$,表示 $a,b$ 之间有一条边,边权为 $c$。保证这些边构成一棵树。
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
|
||||||
|
输出 $k$ 行,每行一个数表示种类为 $i$ 的暴龙的红温值。
|
||||||
|
|
||||||
|
## 样例 #1
|
||||||
|
|
||||||
|
### 样例输入 #1
|
||||||
|
|
||||||
|
```
|
||||||
|
6 3
|
||||||
|
2 3 1 1 2 3
|
||||||
|
1 2 1
|
||||||
|
1 3 1
|
||||||
|
2 5 1
|
||||||
|
3 4 1
|
||||||
|
3 6 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 样例输出 #1
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
```
|
||||||
|
|
||||||
|
## 提示
|
||||||
|
|
||||||
|
【样例 2】
|
||||||
|
|
||||||
|
见选手目录下的 tree/tree2.in 与 tree/tree2.ans。
|
||||||
|
该样例满足子任务 1 的限制。
|
||||||
|
|
||||||
|
【样例 3】
|
||||||
|
|
||||||
|
见选手目录下的 tree/tree3.in 与 tree/tree3.ans。
|
||||||
|
该样例满足子任务 2 的限制。
|
||||||
|
|
||||||
|
【样例 4】
|
||||||
|
|
||||||
|
见选手目录下的 tree/tree4.in 与 tree/tree4.ans。
|
||||||
|
该样例满足子任务 3 的限制。
|
||||||
|
|
||||||
|
【样例 5】
|
||||||
|
|
||||||
|
见选手目录下的 tree/tree5.in 与 tree/tree5.ans。
|
||||||
|
该样例满足子任务 4 的限制。
|
||||||
|
|
||||||
|
|
||||||
|
本题使用捆绑测试。
|
||||||
|
|
||||||
|
| 子任务编号 | $n$ | 分值 |
|
||||||
|
| :----------: | :----------: | :----------: |
|
||||||
|
| 1 |$3000$ | 5 |
|
||||||
|
| 2| $10^5$ | 10|
|
||||||
|
| 3 | $3\times 10^5$ | 10 |
|
||||||
|
| 4 | $5\times 10^5$ | 75 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
对于所有数据,$1\leq p_i \leq k\leq n\leq 5\times 10^5,1\leq a,b\leq n,1\leq c\leq 10^9$。
|
||||||
|
|
||||||
|
本题输入输出量较大,请选手使用较快的 IO 方式。
|
||||||
|
|
||||||
|
|
||||||
|
本题下发文件:[https://git.zziyu.cn/Zengtudor/algorithm_2024/src/branch/main/src/20241118/tree](https://git.zziyu.cn/Zengtudor/algorithm_2024/src/branch/main/src/20241118/tree)(打开后预览)
|
||||||
|
# 25pts暴力代码与解析
|
||||||
|
>关于使用ai
|
||||||
|
>
|
||||||
|
>1. 下面的代码是本人手写
|
||||||
|
>2. 解析由ai生成模型为chatgpt o1-mini
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
if((a[u] == kk || dp[u][kk] > 0) && (a[v] == kk || dp[v][kk] > 0)) {
|
#include <cctype>
|
||||||
ans[kk] = max(ans[kk], dp[u][kk] + dp[v][kk] + w);
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
using ll = int64_t;
|
||||||
|
using std::cin, std::cout;
|
||||||
|
|
||||||
|
// 定义最大节点数为5e5加5,以确保足够的空间
|
||||||
|
const ll maxn = 5e5 + 5;
|
||||||
|
ll n, k; // n: 树的节点数,k: 暴龙的种类数
|
||||||
|
ll a[maxn]; // a[i]: 第i个节点的暴龙种类
|
||||||
|
ll ans[maxn]; // ans[i]: 第i种暴龙的红温值
|
||||||
|
std::vector<std::pair<ll, ll>> adj[maxn]; // adj[u]: 节点u的邻居及边权
|
||||||
|
std::map<ll, ll> dp[maxn]; // dp[u][j]: 节点u到子树中种类j的最大距离
|
||||||
|
|
||||||
|
// 优化输入读取,提高IO速度
|
||||||
|
struct CinNum {
|
||||||
|
char c;
|
||||||
|
ll n, w;
|
||||||
|
// 重载右移运算符以自定义输入方式
|
||||||
|
CinNum &operator>>(ll &num) {
|
||||||
|
c = n = 0;
|
||||||
|
w = 1;
|
||||||
|
// 跳过非数字字符,记录负号
|
||||||
|
while (!isdigit(c = getchar())) {
|
||||||
|
if (c == '-') w = -1;
|
||||||
|
}
|
||||||
|
// 读取数字
|
||||||
|
while (isdigit(c)) {
|
||||||
|
n = n * 10 + (c - '0');
|
||||||
|
c = getchar();
|
||||||
|
}
|
||||||
|
num = n * w;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
} cinn;
|
||||||
|
// 使用自定义的CinNum来替代标准输入
|
||||||
|
#define cin cinn
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用深度优先搜索 (DFS) 计算每种暴龙的红温值
|
||||||
|
*
|
||||||
|
* @param fth 当前节点的父节点
|
||||||
|
* @param u 当前处理的节点
|
||||||
|
*/
|
||||||
|
void dfs(const ll &fth, const ll &u) noexcept {
|
||||||
|
// 初始化当前节点的dp,自己到自己距离为0
|
||||||
|
dp[u].emplace(a[u], 0);
|
||||||
|
|
||||||
|
// 遍历所有邻接节点
|
||||||
|
for (const auto& [v, w] : adj[u]) {
|
||||||
|
if (v == fth) continue; // 避免回到父节点
|
||||||
|
dfs(u, v); // 递归处理子节点
|
||||||
|
|
||||||
|
// 遍历子节点v的所有暴龙种类及其对应的最大距离
|
||||||
|
for (const auto& [kk, vv] : dp[v]) {
|
||||||
|
// 如果当前节点u已经有这种暴龙类型,且子节点v也有
|
||||||
|
if ((dp[u][kk] > 0 || a[u] == kk) && (dp[v][kk] > 0 || a[v] == kk)) {
|
||||||
|
// 更新ans[kk]为u和v子树中这种暴龙类型的最大距离
|
||||||
|
ans[kk] = std::max(ans[kk], dp[u][kk] + dp[v][kk] + w);
|
||||||
|
}
|
||||||
|
// 更新u节点到这种暴龙类型的最大距离
|
||||||
|
dp[u][kk] = std::max(dp[u][kk], dp[v][kk] + w);
|
||||||
|
|
||||||
|
// 如果当前节点u本身就是这种暴龙类型,进一步更新ans[kk]
|
||||||
|
if (a[u] == kk) {
|
||||||
|
ans[kk] = std::max(ans[kk], dp[u][kk]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 清空子节点v的dp以节省内存,因为已经合并到u的dp中了
|
||||||
|
dp[v].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
// 读取节点数n和暴龙种类数k
|
||||||
|
cin >> n >> k;
|
||||||
|
|
||||||
|
// 读取每个节点的暴龙种类
|
||||||
|
for(ll i = 1; i <= n; i++) cin >> a[i];
|
||||||
|
|
||||||
|
// 读取树的边信息,并构建邻接表
|
||||||
|
for(ll i = 1; i < n; i++){
|
||||||
|
ll u, v, w;
|
||||||
|
cin >> u >> v >> w;
|
||||||
|
adj[u].emplace_back(v, w);
|
||||||
|
adj[v].emplace_back(u, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从节点1开始DFS遍历整棵树,假设节点1为根节点,父节点为0
|
||||||
|
dfs(0, 1);
|
||||||
|
|
||||||
|
// 输出每种暴龙的红温值
|
||||||
|
for(ll i = 1; i <= k; i++) cout << ans[i] << '\n';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 变量说明
|
### 代码详解
|
||||||
|
|
||||||
- `kk`: 表示当前正在处理的暴龙类型的标识符(通常是种类编号)。
|
1. **数据结构选择**:
|
||||||
- `a[u]`: 当前节点 `u` 的暴龙类型。
|
- **邻接表**: 使用`std::vector<std::pair<ll, ll>> adj[maxn];`来存储树的邻接表,每个节点存储与之相连的节点及边权。
|
||||||
- `dp[u][kk]`: 从当前节点 `u` 开始的子树中,该暴龙类型 `kk` 的最大距离。
|
- **动态规划表(DP)**: 使用`std::map<ll, ll> dp[maxn];`来存储每个节点到其子树中不同暴龙种类的最大距离。这里使用`std::map`是因为暴龙种类k可能较多且不连续。
|
||||||
- `dp[v][kk]`: 从子节点 `v` 开始的子树中,该暴龙类型 `kk` 的最大距离。
|
|
||||||
- `w`: 当前节点 `u` 和子节点 `v` 之间的边的权重(即两者之间的距离)。
|
|
||||||
- `ans[kk]`: 用于存储当前暴龙类型 `kk` 的红温值(最大距离)。
|
|
||||||
|
|
||||||
### 逻辑解释
|
2. **输入优化**:
|
||||||
|
- 定义了一个自定义的`CinNum`结构体,通过重载`>>`运算符来快速读取整数。这在处理大数据量时能够显著提高输入速度。
|
||||||
|
|
||||||
1. **类型匹配**:
|
3. **深度优先搜索(DFS)**:
|
||||||
- `(a[u] == kk || dp[u][kk] > 0)`:
|
- **初始化**: 对于每个节点u,将自己的暴龙种类`a[u]`加入`dp[u]`,初始距离为0。
|
||||||
- 这个条件检查当前节点 `u` 是否是类型 `kk` 或者在 `u` 的子树中已经存在类型 `kk` 的暴龙。如果存在,可以考虑将 `u` 和 `v` 之间的距离计算入 `ans[kk]`。
|
- **递归处理子节点**: 对每个子节点v,递归调用`dfs(u, v)`。
|
||||||
|
- **合并子节点DP**: 对于子节点v的每个暴龙种类kk,检查当前节点u是否也有这种暴龙。如果有,则计算通过边(u, v)连接后的距离,并更新`ans[kk]`为更大的值。
|
||||||
|
- **更新u节点的DP**: 将子节点v的距离加上边权w,与当前的`dp[u][kk]`比较,取较大者。
|
||||||
|
- **清理资源**: 处理完子节点v后,清空`dp[v]`以节省内存。
|
||||||
|
|
||||||
- `(a[v] == kk || dp[v][kk] > 0)`:
|
4. **输出结果**:
|
||||||
- 这个条件检查子节点 `v` 是否是类型 `kk` 或者在 `v` 的子树中已经存在类型 `kk` 的暴龙。类似地,如果存在,也可以考虑将 `u` 和 `v` 之间的距离计算入 `ans[kk]`。
|
- 遍历每种暴龙种类,输出其对应的红温值。若某种暴龙在树中数量少于2,则红温值保持初始化的0。
|
||||||
|
|
||||||
2. **计算距离**:
|
|
||||||
- 如果以上两个条件都是满足的,表示 `u` 和 `v` 之间的边可以被计算在类型 `kk` 的红温值中,因此最大距离被更新为:
|
|
||||||
```cpp
|
|
||||||
ans[kk] = max(ans[kk], dp[u][kk] + dp[v][kk] + w);
|
|
||||||
```
|
|
||||||
- 这里的 `dp[u][kk] + dp[v][kk] + w` 即为通过当前边 `u-v` 连接的所有类型为 `kk` 的暴龙之间的距离。
|
|
||||||
|
|
||||||
### 总结
|
### 总结
|
||||||
|
|
||||||
这个 `if` 判断的作用在于决定是否可以将当前边 `u-v` 的权重 `w` 加入到 `ans[kk]` 中,从而更新当前暴龙类型 `kk` 的最大红温值。这确保了只有在当前节点及其子节点之间具有相同暴龙类型时,才会合并和计算它们之间的边的影响。这样能够更精确地得到不同类型暴龙之间的距离关系。
|
该算法利用深度优先搜索(DFS)结合动态规划(DP)的思想,高效地计算了树中每种暴龙类型的最大距离(红温值)。具体步骤如下:
|
||||||
|
|
||||||
|
1. **DFS遍历**: 从根节点开始,递归遍历整个树结构。
|
||||||
|
2. **动态规划**: 在遍历的过程中,维护每个节点到其子树中特定暴龙类型的最大距离。
|
||||||
|
3. **答案更新**: 通过合并子节点的信息,动态地更新每种暴龙类型的最大距离。
|
||||||
|
4. **复杂度分析**:
|
||||||
|
- 对于每个节点,可能需要处理最多k种暴龙类型。因此,时间复杂度在最坏情况下为O(nk)。由于k和n均可达5e5,这种方法在大规模数据下会超过时间限制。
|
||||||
|
5. **适用性**:
|
||||||
|
- 该代码适用于子任务1,规模较小(n ≤ 3000)的情况,能够在有限的时间内完成计算。
|
||||||
|
- 对于更大规模的数据,需要优化数据结构和算法,例如使用更高效的方式存储DP信息(如并查集、最低公共祖先LCA等技术),以降低时间和空间复杂度。
|
||||||
|
|
||||||
|
希望这些解释和注释对您理解和优化代码有所帮助!如果有进一步的问题,欢迎继续交流。
|
Loading…
Reference in New Issue
Block a user