《P3038 [USACO11DEC] Grass Planting G》

题目描述

给出一棵有 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;

}