《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 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;

}