题目描述

给出一棵有 n 个节点的树,有 m 个如下所示的操作:

  • 将两个节点之间的 路径上的边 的权值均加一。

  • 查询两个节点之间的 那一条边 的权值,保证两个节点直接相连。

初始边权均为 0。

输入格式

第一行两个整数 n,m,含义如上。

接下来 n−1 行,每行两个整数 u,v,表示 u,v 之间有一条边。

接下来 m 行,每行格式为 op u v,op=P 代表第一个操作,op=Q 代表第二个操作。

输出格式

若干行。对于每个查询操作,输出一行整数,代表查询的答案。

显示翻译

题意翻译

输入输出样例

输入 #1复制

4 6 
1 4 
2 4 
3 4 
P 2 3 
P 1 3 
Q 3 4 
P 1 4 
Q 2 4 
Q 1 4 

输出 #1复制

2 
1 
2 

说明/提示

对于 100% 的数据,2≤n≤105,1≤m≤105。

代码实现:

#include<bits/stdc++.h>
using namespace std;

// 节点数量和操作数量
int nodeCount, operationCount;

// 存储每个节点的父节点
int parentOfNode[100001]; 
// 存储每个节点在树中的深度
int depthOfNode[100001]; 
// 存储每个节点的重儿子节点
int heavySonOfNode[100001]; 
// 存储以每个节点为根的子树的节点数量
int subtreeNodeCount[100001]; 
// 存储每个节点所在重链的顶端节点
int topNodeOfChain[100001]; 
// 存储每个节点在线段树中的位置编号
int segmentTreePosition[100001]; 

// 使用 vector 存储图的邻接表
vector<int> graph[100001]; 

// 第一次深度优先搜索,用于计算节点的深度、重儿子、子树节点数量等信息
void firstDFS(int currentNode, int parentNode, int currentDepth) {
    parentOfNode[currentNode] = parentNode;
    depthOfNode[currentNode] = currentDepth;
    subtreeNodeCount[currentNode] = 1;
    int maxSubtreeSize = 0;

    for (int i = 0; i < graph[currentNode].size(); i++) {
        int adjacentNode = graph[currentNode][i];
        if (adjacentNode!= parentNode) {
            firstDFS(adjacentNode, currentNode, currentDepth + 1);
            subtreeNodeCount[currentNode] += subtreeNodeCount[adjacentNode];
            if (maxSubtreeSize < subtreeNodeCount[adjacentNode]) {
                heavySonOfNode[currentNode] = adjacentNode;
                maxSubtreeSize = subtreeNodeCount[adjacentNode];
            }
        }
    }
}

// 第二次深度优先搜索,用于构建重链剖分
void secondDFS(int currentNode, int chainTopNode) {
    topNodeOfChain[currentNode] = chainTopNode;
    segmentTreePosition[currentNode] = ++segmentTreePosition[0];
    if (!heavySonOfNode[currentNode]) return;
    secondDFS(heavySonOfNode[currentNode], chainTopNode);

    for (int i = 0; i < graph[currentNode].size(); i++) {
        int adjacentNode = graph[currentNode][i];
        if (parentOfNode[currentNode] == adjacentNode || heavySonOfNode[currentNode] == adjacentNode) continue;
        secondDFS(adjacentNode, adjacentNode);
    }
}

// 线段树节点结构体
struct SegmentTreeNode {
    int leftBound;
    int rightBound;
    int sumValue;
    int lazyAddValue;
};

SegmentTreeNode segmentTree[400001];

// 构建线段树
void buildSegmentTree(int treeNodeIndex, int left, int right) {
    segmentTree[treeNodeIndex].leftBound = left;
    segmentTree[treeNodeIndex].rightBound = right;
    if (left == right) return;
    buildSegmentTree(treeNodeIndex * 2, left, (left + right) / 2);
    buildSegmentTree(treeNodeIndex * 2 + 1, (left + right) / 2 + 1, right);
}

// 下传线段树的懒标记
void pushDownLazyTag(int treeNodeIndex) {
    segmentTree[treeNodeIndex * 2].lazyAddValue += segmentTree[treeNodeIndex].lazyAddValue;
    segmentTree[treeNodeIndex * 2 + 1].lazyAddValue += segmentTree[treeNodeIndex].lazyAddValue;
    segmentTree[treeNodeIndex * 2].sumValue += (segmentTree[treeNodeIndex * 2].rightBound - segmentTree[treeNodeIndex * 2].leftBound + 1) * segmentTree[treeNodeIndex].lazyAddValue;
    segmentTree[treeNodeIndex * 2 + 1].sumValue += (segmentTree[treeNodeIndex * 2 + 1].rightBound - segmentTree[treeNodeIndex * 2 + 1].leftBound + 1) * segmentTree[treeNodeIndex].lazyAddValue;
    segmentTree[treeNodeIndex].lazyAddValue = 0;
}

// 在线段树上进行区间修改操作
void updateSegmentTree(int treeNodeIndex, int left, int right) {
    if (segmentTree[treeNodeIndex].rightBound < left || segmentTree[treeNodeIndex].leftBound > right) return;
    if (segmentTree[treeNodeIndex].leftBound >= left && segmentTree[treeNodeIndex].rightBound <= right) {
        segmentTree[treeNodeIndex].sumValue += segmentTree[treeNodeIndex].rightBound - segmentTree[treeNodeIndex].leftBound + 1;
        segmentTree[treeNodeIndex].lazyAddValue++;
        return;
    }
    pushDownLazyTag(treeNodeIndex);
    updateSegmentTree(treeNodeIndex * 2, left, right);
    updateSegmentTree(treeNodeIndex * 2 + 1, left, right);
    segmentTree[treeNodeIndex].sumValue = segmentTree[treeNodeIndex * 2].sumValue + segmentTree[treeNodeIndex * 2 + 1].sumValue;
}

// 在线段树上查询单点的值
int querySegmentTree(int treeNodeIndex, int targetPosition) {
    if (segmentTree[treeNodeIndex].rightBound < targetPosition || segmentTree[treeNodeIndex].leftBound > targetPosition) return 0;
    if (segmentTree[treeNodeIndex].leftBound == segmentTree[treeNodeIndex].rightBound) return segmentTree[treeNodeIndex].sumValue;
    pushDownLazyTag(treeNodeIndex);
    return querySegmentTree(treeNodeIndex * 2, targetPosition) + querySegmentTree(treeNodeIndex * 2 + 1, targetPosition);
}

int main() {
    int node1, node2;
    char operationType;
    cin >> nodeCount >> operationCount;
    for (int i = 1; i < nodeCount; i++) {
        cin >> node1 >> node2;
        graph[node1].push_back(node2);
        graph[node2].push_back(node1); // 存储无向边
    }

    firstDFS(1, 0, 1);
    secondDFS(1, 1);
    buildSegmentTree(1, 1, nodeCount);

    while (operationCount--) {
        cin >> operationType >> node1 >> node2;
        if (operationType == 'P') {
            while (topNodeOfChain[node1]!= topNodeOfChain[node2]) {
                if (depthOfNode[topNodeOfChain[node1]] < depthOfNode[topNodeOfChain[node2]]) swap(node1, node2);
                updateSegmentTree(1, segmentTreePosition[topNodeOfChain[node1]], segmentTreePosition[node1]);
                node1 = parentOfNode[topNodeOfChain[node1]];
            }
            if (depthOfNode[node1] > depthOfNode[node2]) swap(node1, node2);
            updateSegmentTree(1, segmentTreePosition[node1] + 1, segmentTreePosition[node2]); // 避开最近公共祖先
        } else {
            if (parentOfNode[node1] == node2) cout << querySegmentTree(1, segmentTreePosition[node1]) << endl;
            else cout << querySegmentTree(1, segmentTreePosition[node2]) << endl; // 判断该边的边权是哪个点的点权
        }
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88604.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88604.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88604.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NestJS

文章的地址 NestJShttps://equinox-primrose-ceb.notion.site/NestJS-22d4b8031e0f80b39fc7fe1ff111f802 不产生测试的.spec.ts文件的配置 "generateOptions": {"spec": false }创建模型 nest g m xx 创建服务 nest g s xx 创建处理 nest g c xx CRU…

vue入门学习教程

一、介绍 vue是一款用于构建用户界面的 JavaScript 框架。基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。 二、使用和安装 方法1&#xff1a;在html代码中直接使用<script>导入&…

C++类对象多态基础语法【超详细】

文章目录前言1. 虚函数1.1 现象1.2 多态1.3 析构函数1.4 override和final1.5 重载、隐藏、重写对比2. 抽象类2.1 抽象类特性2.2 抽象类的应用场景3. 多态实现的底层原理4. 静态绑定和动态绑定5. 总结前言 多态是面向对象三大特性之一&#xff0c;也是细节最多的语法之一。学习…

Flask 入门到实战(3):用 SQLAlchemy 优雅操作数据库

深入理解 Flask ORM&#xff1a;用 SQLAlchemy 优雅操作数据库一、前言&#xff1a;什么是 ORM&#xff1f;为什么要用它&#xff1f; 传统数据库操作要写 SQL&#xff0c;比如&#xff1a; SELECT * FROM users WHERE id 1;而使用 ORM 后&#xff0c;你可以这样写&#xff1a…

源表=电源+数字表?一文看懂SMU源表 2025-04-14

源表(Source Meter Unit, SMU)广泛用于半导体器件、材料、医疗、发光器件与光通信等行业,测量器件的伏安(I-V)特性曲线、绝缘材料的电阻值(电阻率)、电容的绝缘电阻(漏电流)、光电器件的暗电流或者L-I-V等。 源表的名称已经清晰的告诉我们,它包含了高精度电源输出和…

单片机STM32F103:DMA的原理以及应用

STM32F103系列微控制器&#xff08;基于ARM Cortex-M3内核&#xff09;集成了**DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;**控制器&#xff0c;用于在存储器与外设、存储器与存储器之间高效传输数据&#xff0c;减少CPU的干预&#xff0c;从而…

Webview 中可用的 VS Code 方法

在 VS Code Webview 的 HTML 中&#xff0c;不能直接调用 VS Code 的 API&#xff08;如 vscode.window.showInformationMessage&#xff09;&#xff0c;但可以通过 acquireVsCodeApi() 获取一个受限的 vscode 对象&#xff0c;用于与插件主程序通信。以下是详细说明和示例&am…

Qt:布局管理器Layout

目录 布局管理器 QVBoxLayout QHBoxLayout QGirdLayout QFormLayout Spacer 布局管理器 在以往的界面操作上&#xff0c;都是程序员手动拖动控件来布局&#xff0c;这种方式有一些不足之处&#xff0c;比如不能很好的把握控件之间的距离&#xff0c;以及控件的大小&…

【Java编程动手学】深入剖析Java网络编程:原理、协议与应用

文章目录一、引言二、计算机网络基础1、计算机网络的概念2、网络地址的重要性三、套接字编程&#xff1a;网络通信的基石1、套接字的概念2、TCP通信编程示例四、TCP通信编程&#xff1a;可靠的数据传输1、TCP协议的特点2、实际应用中的TCP通信五、UDP通信编程&#xff1a;高效的…

vue3.2 前端动态分页算法

文章目录背景思路页面情况核心代码小结效果背景 1. 后台接口只是动态返回一个数组的数据&#xff0c;前端需要根据数据量的大小判断是否需要分页&#xff0c;页面高度固定2. 页面根据页数大小有不同的展示a. 只有一页 头部 内容 统计 尾部b. 多页i. 第一页 头部 内容 尾…

UC浏览器PC版自2016年后未再更新不支持vue3

win uc浏览器&#xff0c;点击页面触发异常。UC浏览器PC版自2016年后未再更新&#xff08;最新版本停留在Chromium 50内核&#xff09;。其内置内核版本较低&#xff08;如Trident/Blink旧版&#xff09;&#xff0c;无法支持Vue 3等现代前端框架的语法特性&#xff08;如ES6、…

亚古数据:澳大利亚公司的ABN和ACN号码是什么?

在跨国商业的迷宫中&#xff0c;了解目标市场的公司注册细节是一项不可或缺的技能。对于与中国企业有业务往来的朋友们来说&#xff0c;澳大利亚这片充满机遇的土地上&#xff0c;两个缩写——ABN与ACN&#xff0c;如同解锁合作之门的密钥&#xff0c;显得尤为重要。今天&#…

LangChain框架 Prompts、Agents 应用

目录 (Prompts)提示作用 Prompts 常见操作 基础 PromptTemplate 使用 Few-shot 提示模板 ChatPromptTemplate (对话提示模板) (Agents)代理作用 Agents 常见操作 基础 Agent 使用 自定义工具 Agent 高级应用示例 带记忆的对话代理 使用本地模型的代理 结构化输出代…

模拟实现unordered_map

1.定义unordered_map 是 C 标准库中的哈希表容器&#xff0c;特点是无序存储、平均 O (1) 时间复杂度的插入 / 查找 / 删除操作。其核心原理是通过哈希函数将关键字映射到哈希桶&#xff08;bucket&#xff09;&#xff0c;再通过链表或红黑树处理哈希冲突。2.实现原理1. 哈希表…

史上最详细Java并发多线程(面试必备,一篇足矣)

第一章&#xff1a;线程基础 1.1 线程与进程 进程&#xff1a;系统资源分配的基本单位&#xff0c;拥有独立的内存空间 线程&#xff1a;CPU调度的基本单位&#xff0c;共享进程内存空间 关系&#xff1a;一个进程可包含多个线程&#xff0c;线程切换成本远低于进程 1.2 线程的…

【DataFlow】数据合成流水线工具

1.整体解读 核心思想&#xff1a;以数据为中心的AI&#xff08;Data-Centric AI&#xff09; DataFlow 的核心目标是通过一系列自动化“流水线”&#xff08;Pipelines&#xff09;来处理和生成高质量的数据&#xff0c;从而提升大语言模型&#xff08;LLM&#xff09;在特定领…

Hangfire 调用报错解决方案总结

System.ArgumentNullException: 值不能为 null 错误在使用 Hangfire 时确实是一个常见问题&#xff0c;特别是在配置 Hangfire 服务器时。问题分析这个错误通常发生在以下情况&#xff1a;没有正确配置 Hangfire 服务器队列配置缺失或不正确连接字符串配置问题解决方案要点正确…

MySQL的使用

MySQL的使用一、mysql中的周边命令1. 检查版本2. 查看字符集3. 查看客户端连接4. 查看最后一条警告消息二、数据库、数据表的管理1. 语法规则2. 数据库2.1 查看数据库2.2 创建数据库2.3 选择数据库2.4 查看创建数据库命令2.5 创建库时添加字符集2.6 修改数据库字符集2.7 删除数…

2025Nginx最新版讲解/面试

维护系统多服务器部署&#xff0c;将我们请求代理到各个服务器。代理正向代理&#xff0c;代理对象是我们的客户端&#xff0c;目标对象不知道我们用户。VPN就是典型的正向代理。反向代理&#xff0c;代理对象是服务端&#xff0c;用户不知道服务端具体信息。而这正是Nginx所做…

JAVASCRIPT 前端数据库-V8--仙盟数据库架构-—-—仙盟创梦IDE

老版本 在v1 版本中我们讲述了 基础版的应用 JAVASCRIPT 前端数据库-V1--仙盟数据库架构-—-—仙盟创梦IDE-CSDN博客 接下载我们做一个更复杂的的其他场景 由于&#xff0c;V1查询字段必须 id 接下来我们修改了了代码 JAVASCRIPT 前端数据库-V2--仙盟数据库架构-—-—仙盟创…