题目描述
给出一棵有 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;
}