《P3313 [SDOI2014] 旅行》
目描述
S 国有 N 个城市,编号从 1 到 N。城市间用 N−1 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。
为了方便,我们用不同的正整数代表各种宗教,S 国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S 国为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
在 S 国的历史上常会发生以下几种事件:
CC x c
:城市 x 的居民全体改信了 c 教;CW x w
:城市 x 的评级调整为 w;QS x y
:一位旅行者从城市 x 出发,到城市 y,并记下了途中留宿过的城市的评级总和;QM x y
:一位旅行者从城市 x 出发,到城市 y,并记下了途中留宿过的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。
输入格式
输入的第一行包含整数 N,Q 依次表示城市数和事件数。
接下来 N 行,第 i+1 行两个整数 Wi,Ci 依次表示记录开始之前,城市 i 的评级和信仰。
接下来 N−1 行每行两个整数 x,y 表示一条双向道路。
接下来 Q 行,每行一个操作,格式如上所述。
输出格式
对每个 QS
和 QM
事件,输出一行,表示旅行者记下的数字。
输入输出样例
输入 #1复制
5 6 3 1 2 3 1 2 3 3 5 1 1 2 1 3 3 4 3 5 QS 1 5 CC 3 1 QS 1 5 CW 3 3 QS 1 5 QM 2 4
输出 #1复制
8 9 11 3
说明/提示
对于 100% 的数据,N,Q≤105,C≤105
数据保证对所有 QS
和 QM
事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于 104 的正整数,且宗教值不大于 C。
代码实现:
#include <bits/stdc++.h>
using namespace std;
#define mid ((left+right)>>1)
const int MAXN = 110000;
char operation[3];
int cityCount, queryCount;
int cityRating[MAXN], cityReligion[MAXN];
int adjacencyHead[MAXN], edgeCount, nodeCount;
int heavySon[MAXN], subtreeSize[MAXN], parent[MAXN], depth[MAXN];
int nodeID[MAXN], currentID, ratingValue[MAXN], chainTop[MAXN], segmentRoot[MAXN];
struct Edge {
int next, to;
} edges[MAXN << 1];
struct SegmentTreeNode {
int leftChild, rightChild;
int sum, maxValue;
bool flag;
} treeNodes[MAXN << 4];
inline void addEdge(int from, int to) {
edges[++edgeCount].next = adjacencyHead[from];
adjacencyHead[from] = edgeCount;
edges[edgeCount].to = to;
}
void dfsForHeavyLightDecomposition1(int currentNode, int parentNode) {
depth[currentNode] = depth[parentNode] + 1;
parent[currentNode] = parentNode;
subtreeSize[currentNode] = 1;
for (int i = adjacencyHead[currentNode]; i; i = edges[i].next) {
int neighbor = edges[i].to;
if (neighbor == parentNode) continue;
dfsForHeavyLightDecomposition1(neighbor, currentNode);
subtreeSize[currentNode] += subtreeSize[neighbor];
if (subtreeSize[neighbor] > subtreeSize[heavySon[currentNode]])
heavySon[currentNode] = neighbor;
}
}
void dfsForHeavyLightDecomposition2(int currentNode, int topNode) {
nodeID[currentNode] = ++currentID;
ratingValue[currentID] = cityRating[currentNode];
chainTop[currentNode] = topNode;
if (!heavySon[currentNode]) return;
dfsForHeavyLightDecomposition2(heavySon[currentNode], topNode);
for (int i = adjacencyHead[currentNode]; i; i = edges[i].next) {
int neighbor = edges[i].to;
if (neighbor != heavySon[currentNode] && neighbor != parent[currentNode])
dfsForHeavyLightDecomposition2(neighbor, neighbor);
}
}
inline void updateParentNode(int nodeIndex) {
treeNodes[nodeIndex].sum = treeNodes[treeNodes[nodeIndex].leftChild].sum +
treeNodes[treeNodes[nodeIndex].rightChild].sum;
treeNodes[nodeIndex].maxValue = max(treeNodes[treeNodes[nodeIndex].leftChild].maxValue,
treeNodes[treeNodes[nodeIndex].rightChild].maxValue);
}
void insertNode(int position, int left, int right, int ¤tNode, int value) {
if (!currentNode) currentNode = ++nodeCount;
treeNodes[currentNode].maxValue = max(treeNodes[currentNode].maxValue, value);
treeNodes[currentNode].sum += value;
if (left == right) return;
if (position <= mid)
insertNode(position, left, mid, treeNodes[currentNode].leftChild, value);
else
insertNode(position, mid + 1, right, treeNodes[currentNode].rightChild, value);
}
void removeNode(int position, int left, int right, int ¤tNode) {
if (left == right) {
treeNodes[currentNode].sum = treeNodes[currentNode].maxValue = 0;
return;
}
if (position <= mid)
removeNode(position, left, mid, treeNodes[currentNode].leftChild);
else
removeNode(position, mid + 1, right, treeNodes[currentNode].rightChild);
updateParentNode(currentNode);
}
int querySum(int queryLeft, int queryRight, int left, int right, int currentNode) {
if (left >= queryLeft && right <= queryRight)
return treeNodes[currentNode].sum;
int result = 0;
if (queryLeft <= mid)
result += querySum(queryLeft, queryRight, left, mid, treeNodes[currentNode].leftChild);
if (queryRight > mid)
result += querySum(queryLeft, queryRight, mid + 1, right, treeNodes[currentNode].rightChild);
return result;
}
int queryPathSum(int startNode, int endNode, int religion) {
int result = 0;
while (chainTop[startNode] != chainTop[endNode]) {
if (depth[chainTop[startNode]] < depth[chainTop[endNode]])
swap(startNode, endNode);
result += querySum(nodeID[chainTop[startNode]], nodeID[startNode], 1, cityCount, segmentRoot[religion]);
startNode = parent[chainTop[startNode]];
}
if (depth[startNode] > depth[endNode])
swap(startNode, endNode);
result += querySum(nodeID[startNode], nodeID[endNode], 1, cityCount, segmentRoot[religion]);
return result;
}
int queryMax(int queryLeft, int queryRight, int left, int right, int currentNode) {
if (left >= queryLeft && right <= queryRight)
return treeNodes[currentNode].maxValue;
int result = 0;
if (queryLeft <= mid)
result = max(result, queryMax(queryLeft, queryRight, left, mid, treeNodes[currentNode].leftChild));
if (queryRight > mid)
result = max(result, queryMax(queryLeft, queryRight, mid + 1, right, treeNodes[currentNode].rightChild));
return result;
}
int queryPathMax(int startNode, int endNode, int religion) {
int result = 0;
while (chainTop[startNode] != chainTop[endNode]) {
if (depth[chainTop[startNode]] < depth[chainTop[endNode]])
swap(startNode, endNode);
result = max(result, queryMax(nodeID[chainTop[startNode]], nodeID[startNode], 1, cityCount, segmentRoot[religion]));
startNode = parent[chainTop[startNode]];
}
if (depth[startNode] > depth[endNode])
swap(startNode, endNode);
result = max(result, queryMax(nodeID[startNode], nodeID[endNode], 1, cityCount, segmentRoot[religion]));
return result;
}
int main() {
ios::sync_with_stdio(false);
cin >> cityCount >> queryCount;
for (int i = 1; i <= cityCount; i++)
cin >> cityRating[i] >> cityReligion[i];
for (int i = 1, u, v; i < cityCount; i++) {
cin >> u >> v;
addEdge(u, v);
addEdge(v, u);
}
dfsForHeavyLightDecomposition1(1, 0);
dfsForHeavyLightDecomposition2(1, 1);
for (int i = 1; i <= cityCount; i++)
insertNode(nodeID[i], 1, cityCount, segmentRoot[cityReligion[i]], cityRating[i]);
for (int i = 1, x, y; i <= queryCount; i++) {
cin >> operation >> x >> y;
if (operation[1] == 'C') {
removeNode(nodeID[x], 1, cityCount, segmentRoot[cityReligion[x]]);
insertNode(nodeID[x], 1, cityCount, segmentRoot[y], cityRating[x]);
cityReligion[x] = y;
}
else if (operation[1] == 'W') {
removeNode(nodeID[x], 1, cityCount, segmentRoot[cityReligion[x]]);
insertNode(nodeID[x], 1, cityCount, segmentRoot[cityReligion[x]], y);
cityRating[x] = y;
}
else if (operation[1] == 'S') {
printf("%d\n", queryPathSum(x, y, cityReligion[x]));
}
else if (operation[1] == 'M') {
printf("%d\n", queryPathMax(x, y, cityReligion[x]));
}
}
return 0;
}