> 技术文档 > 《P3313 [SDOI2014] 旅行》

《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 63 12 31 23 35 11 21 33 43 5QS 1 5CC 3 1QS 1 5CW 3 3QS 1 5QM 2 4

输出 #1复制

89113

说明/提示

对于 100% 的数据,N,Q≤105,C≤105

数据保证对所有 QS 和 QM 事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于 104 的正整数,且宗教值不大于 C。

代码实现:

#include
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 &currentNode, 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 &currentNode) {
    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;
}