S国有 N N 个城市,编号从1 1 N N 。城市间用N1 N - 1 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。

在S国的历史上常会发生以下几种事件:
CC x c:城市x x 的居民全体改信了c c 教;
CW x w:城市x x 的评级调整为c c ;
QS x y:一位旅行者从城市x x 出发,到城市y y ,并记下了途中留宿过的城市的评级总和;
QM x y:一位旅行者从城市x x 出发,到城市y y ,并记下了途中留宿过的城市的评级最大值。

由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

题解

本题的主要限制在宗教,做法是对于每个宗教开一棵线段树。

显然地,如果一开始就把 c c 棵线段树建好,那么一定会 MLE,所以我们要用线段树动态开点来搞,大意就是每次插入的时候沿路新建不存在的节点。

写法就是把线段树中的节点在修改中下传,若该节点为空,则新建它。

更改宗教时,将一个节点在原宗教线段树中的值设为 0 0 ,再将其插入新宗教线段树中。

顺便说一句,更改某个节点的值时不要只修改它在线段树中的值,别问我是怎么知道的。

代码

#include <cstdio>
#include <cstring>
#include <climits>
#include <stack>
#include <algorithm>
const int MAX_N = 100000;
const int MAX_C = 100000;
const int MAX_NODE_NUM = 3400000;
struct SegmentTree {
#define mid ((l + r) >> 1)
struct Node {
Node *lc, *rc;
int sum, max;
void init() {
lc = rc = NULL;
sum = max = 0;
}
void update() {
sum = (lc ? lc->sum : 0) + (rc ? rc->sum : 0);
max = std::max(lc ? lc->max : 0, rc ? rc->max : 0);
}
};
static Node *newNode() {
static Node N[MAX_NODE_NUM], *_end = N;
return _end->init(), _end++;
}
Node *root;
int L, R;
SegmentTree(int l, int r) {
L = l, R = r, root = NULL;
}
int querySum(Node *v, int l, int r, int ql, int qr) {
if (v == NULL) return 0;
else if (l == ql && r == qr) return v->sum;
else if (mid >= qr) return querySum(v->lc, l, mid, ql, qr);
else if (mid <= ql) return querySum(v->rc, mid, r, ql, qr);
else return querySum(v->lc, l, mid, ql, mid) + querySum(v->rc, mid, r, mid, qr);
}
int queryMax(Node *v, int l, int r, int ql, int qr) {
if (v == NULL) return 0;
else if (l == ql && r == qr) return v->max;
else if (mid >= qr) return queryMax(v->lc, l, mid, ql, qr);
else if (mid <= ql) return queryMax(v->rc, mid, r, ql, qr);
else return std::max(queryMax(v->lc, l, mid, ql, mid), queryMax(v->rc, mid, r, mid, qr));
}
void modify(Node *&v, int l, int r, int pos, int val) {
if (v == NULL) v = newNode();
if (r - l == 1) v->sum = v->max = val;
else {
if (pos < mid) modify(v->lc, l, mid, pos, val);
else modify(v->rc, mid, r, pos, val);
v->update();
}
}
int querySum(int l, int r) {
return querySum(root, L, R, l, r);
}
int queryMax(int l, int r) {
return queryMax(root, L, R, l, r);
}
void modify(int pos, int val) {
modify(root, L, R, pos, val);
}
} *segt[MAX_C + 1];
struct Node;
struct Edge;
struct Node {
Edge *e;
Node *fa, *maxChild, *pathFa;
int dfn, depth, size;
bool vis;
int type, val;
} N[MAX_N + 5];
struct Edge {
Node *fr, *to;
Edge *ne;
Edge() {}
Edge(Node *fr, Node *to) : fr(fr), to(to), ne(fr->e) {}
};
inline void addEdge(int fr, int to) {
Node *u = &N[fr], *v = &N[to];
u->e = new Edge(u, v);
v->e = new Edge(v, u);
}
int n, c;
inline void treeChainSplit(Node *root) {
std::stack<Node *> s;
s.push(root), root->fa = NULL, root->depth = 0;
while (!s.empty()) {
Node *v = s.top();
if (!v->vis) {
for (Edge *e = v->e; e; e = e->ne) {
if (e->to != v->fa) {
e->to->fa = v, e->to->depth = v->depth + 1;
s.push(e->to);
}
}
v->vis = true;
} else {
v->size = 1, v->maxChild = NULL;
for (Edge *e = v->e; e; e = e->ne) {
if (e->to != v->fa) {
v->size += e->to->size;
if (!v->maxChild || v->maxChild->size < e->to->size) v->maxChild = e->to;
}
}
s.pop();
}
}
for (int i = 0; i < n; i++) N[i].vis = false;
int timeStamp = 0;
s.push(root);
while (!s.empty()) {
Node *v = s.top();
if (!v->vis) {
v->dfn = timeStamp++;
for (Edge *e = v->e; e; e = e->ne) {
if (e->to != v->fa && e->to != v->maxChild) {
s.push(e->to);
}
}
if (v->maxChild) s.push(v->maxChild);
v->pathFa = (!v->fa || v != v->fa->maxChild) ? v : v->fa->pathFa;
v->vis = true;
} else {
s.pop();
}
}
}
inline void build() {
treeChainSplit(N);
for (int i = 1; i <= MAX_C; i++) segt[i] = new SegmentTree(0, n);
for (int i = 0; i < n; i++) segt[N[i].type]->modify(N[i].dfn, N[i].val);
}
inline void modifyType(int i, int x) {
Node *v = &N[i];
segt[v->type]->modify(v->dfn, 0);
segt[v->type = x]->modify(v->dfn, v->val);
}
inline void modifyVal(int i, int val) {
Node *v = &N[i];
segt[v->type]->modify(v->dfn, v->val = val);
}
inline int querySum(int x, int y) {
Node *u = &N[x], *v = &N[y];
SegmentTree *s = segt[u->type];
int res = 0;
while (u->pathFa != v->pathFa) {
if (u->pathFa->depth < v->pathFa->depth) std::swap(u, v);
res += s->querySum(u->pathFa->dfn, u->dfn + 1);
u = u->pathFa->fa;
}
if (u->depth > v->depth) std::swap(u, v);
res += s->querySum(u->dfn, v->dfn + 1);
return res;
}
inline int queryMax(int x, int y) {
Node *u = &N[x], *v = &N[y];
SegmentTree *s = segt[u->type];
int res = INT_MIN;
while (u->pathFa != v->pathFa) {
if (u->pathFa->depth < v->pathFa->depth) std::swap(u, v);
res = std::max(res, s->queryMax(u->pathFa->dfn, u->dfn + 1));
u = u->pathFa->fa;
}
if (u->depth > v->depth) std::swap(u, v);
res = std::max(res, s->queryMax(u->dfn, v->dfn + 1));
return res;
}
int main() {
int q;
scanf("%d %d", &n, &q);
for (int i = 0; i < n; i++) {
scanf("%d %d", &N[i].val, &N[i].type);
}
for (int i = 0, u, v; i < n - 1; i++) {
scanf("%d %d", &u, &v), u--, v--;
addEdge(u, v);
}
build();
static char cmd[3];
for (int i = 0, x, y; i < q; i++) {
scanf("%s %d %d", cmd, &x, &y);
if (cmd[0] == 'C') {
x--;
if (cmd[1] == 'C') modifyType(x, y);
else if (cmd[1] == 'W') modifyVal(x, y);
else throw "%%% Menci";
} else if (cmd[0] == 'Q') {
x--, y--;
if (cmd[1] == 'S') printf("%d\n", querySum(x, y));
else if (cmd[1] == 'M') printf("%d\n", queryMax(x, y));
else throw "%%% Menci";
} else throw "%%% Menci";
}
return 0;
}