题目描述
给出一棵有 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 parentOfNode100001;
// 存储每个节点在树中的深度
int depthOfNode100001;
// 存储每个节点的重儿子节点
int heavySonOfNode100001;
// 存储以每个节点为根的子树的节点数量
int subtreeNodeCount100001;
// 存储每个节点所在重链的顶端节点
int topNodeOfChain100001;
// 存储每个节点在线段树中的位置编号
int segmentTreePosition100001;
// 使用 vector 存储图的邻接表
vector<int> graph100001;
// 第一次深度优先搜索,用于计算节点的深度、重儿子、子树节点数量等信息
void firstDFS(int currentNode, int parentNode, int currentDepth) {
parentOfNodecurrentNode = parentNode;
depthOfNodecurrentNode = currentDepth;
subtreeNodeCountcurrentNode = 1;
int maxSubtreeSize = 0;
for (int i = 0; i < graphcurrentNode.size(); i++) {
int adjacentNode = graphcurrentNodei;
if (adjacentNode!= parentNode) {
firstDFS(adjacentNode, currentNode, currentDepth + 1);
subtreeNodeCountcurrentNode += subtreeNodeCountadjacentNode;
if (maxSubtreeSize < subtreeNodeCountadjacentNode) {
heavySonOfNodecurrentNode = adjacentNode;
maxSubtreeSize = subtreeNodeCountadjacentNode;
}
}
}
}
// 第二次深度优先搜索,用于构建重链剖分
void secondDFS(int currentNode, int chainTopNode) {
topNodeOfChaincurrentNode = chainTopNode;
segmentTreePositioncurrentNode = ++segmentTreePosition0;
if (!heavySonOfNodecurrentNode) return;
secondDFS(heavySonOfNodecurrentNode, chainTopNode);
for (int i = 0; i < graphcurrentNode.size(); i++) {
int adjacentNode = graphcurrentNodei;
if (parentOfNodecurrentNode == adjacentNode || heavySonOfNodecurrentNode == adjacentNode) continue;
secondDFS(adjacentNode, adjacentNode);
}
}
// 线段树节点结构体
struct SegmentTreeNode {
int leftBound;
int rightBound;
int sumValue;
int lazyAddValue;
};
SegmentTreeNode segmentTree400001;
// 构建线段树
void buildSegmentTree(int treeNodeIndex, int left, int right) {
segmentTreetreeNodeIndex.leftBound = left;
segmentTreetreeNodeIndex.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) {
segmentTreetreeNodeIndex \* 2.lazyAddValue += segmentTreetreeNodeIndex.lazyAddValue;
segmentTreetreeNodeIndex \* 2 + 1.lazyAddValue += segmentTreetreeNodeIndex.lazyAddValue;
segmentTreetreeNodeIndex \* 2.sumValue += (segmentTreetreeNodeIndex \* 2.rightBound - segmentTreetreeNodeIndex \* 2.leftBound + 1) * segmentTreetreeNodeIndex.lazyAddValue;
segmentTreetreeNodeIndex \* 2 + 1.sumValue += (segmentTreetreeNodeIndex \* 2 + 1.rightBound - segmentTreetreeNodeIndex \* 2 + 1.leftBound + 1) * segmentTreetreeNodeIndex.lazyAddValue;
segmentTreetreeNodeIndex.lazyAddValue = 0;
}
// 在线段树上进行区间修改操作
void updateSegmentTree(int treeNodeIndex, int left, int right) {
if (segmentTreetreeNodeIndex.rightBound < left || segmentTreetreeNodeIndex.leftBound > right) return;
if (segmentTreetreeNodeIndex.leftBound >= left && segmentTreetreeNodeIndex.rightBound <= right) {
segmentTreetreeNodeIndex.sumValue += segmentTreetreeNodeIndex.rightBound - segmentTreetreeNodeIndex.leftBound + 1;
segmentTreetreeNodeIndex.lazyAddValue++;
return;
}
pushDownLazyTag(treeNodeIndex);
updateSegmentTree(treeNodeIndex * 2, left, right);
updateSegmentTree(treeNodeIndex * 2 + 1, left, right);
segmentTreetreeNodeIndex.sumValue = segmentTreetreeNodeIndex \* 2.sumValue + segmentTreetreeNodeIndex \* 2 + 1.sumValue;
}
// 在线段树上查询单点的值
int querySegmentTree(int treeNodeIndex, int targetPosition) {
if (segmentTreetreeNodeIndex.rightBound < targetPosition || segmentTreetreeNodeIndex.leftBound > targetPosition) return 0;
if (segmentTreetreeNodeIndex.leftBound == segmentTreetreeNodeIndex.rightBound) return segmentTreetreeNodeIndex.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;
graphnode1.push_back(node2);
graphnode2.push_back(node1); // 存储无向边
}
firstDFS(1, 0, 1);
secondDFS(1, 1);
buildSegmentTree(1, 1, nodeCount);
while (operationCount--) {
cin >> operationType >> node1 >> node2;
if (operationType == 'P') {
while (topNodeOfChainnode1!= topNodeOfChainnode2) {
if (depthOfNodetopNodeOfChain\[node1] < depthOfNodetopNodeOfChain\[node2]) swap(node1, node2);
updateSegmentTree(1, segmentTreePositiontopNodeOfChain\[node1], segmentTreePositionnode1);
node1 = parentOfNodetopNodeOfChain\[node1];
}
if (depthOfNodenode1 > depthOfNodenode2) swap(node1, node2);
updateSegmentTree(1, segmentTreePositionnode1 + 1, segmentTreePositionnode2); // 避开最近公共祖先
} else {
if (parentOfNodenode1 == node2) cout << querySegmentTree(1, segmentTreePositionnode1) << endl;
else cout << querySegmentTree(1, segmentTreePositionnode2) << endl; // 判断该边的边权是哪个点的点权
}
}
return 0;
}