\(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\) ,并记下了途中留宿过的城市的评级最大值。由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。
分析
如果没有宗教属性,那么这道题是一道裸的树链剖分,但是有了它,该怎么办?给每个宗教建一棵线段树?空间肯定爆炸。于是我们可以参考主席树的思想,动态开点,然后用每个结点更新它对应的宗教的线段树,而修改操作,如果是修改宗教,我们还需要删除操作,其他的都是常规操作。
一些闲话。辣鸡 \(strcmp\) ,害我 MLE 了无数发。
代码
#include <bits/stdc++.h>
#define N 100003
#define rg register
using namespace std;
int gi() {
rg int x = 0, f = 1; rg char c = getchar();
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
void chkmax(int &x, int y) { if (x < y) x = y; }
struct SegmentTree {
int ls, rs, sum, mx;
} t[N << 5];
int n, q, tot, num;
int w[N], c[N], rt[N];
int to[N << 1], nxt[N << 1], hd[N], cnt;
int dep[N], fa[N], dfn[N], rk[N], sz[N], son[N], top[N];
void insert(int u, int v) { to[++cnt] = v, nxt[cnt] = hd[u], hd[u] = cnt; }
void dfs1(int u, int f) {
dep[u] = dep[f] + 1, fa[u] = f, sz[u] = 1;
for (int i = hd[u]; i; i = nxt[i]) {
int v = to[i];
if (v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
dfn[u] = ++tot, rk[tot] = u, top[u] = tp;
if (son[u]) dfs2(son[u], tp);
for (int i = hd[u]; i; i = nxt[i]) {
if (to[i] != fa[u] && to[i] != son[u])
dfs2(to[i], to[i]);
}
}
void pushup(int o) {
t[o].sum = t[t[o].ls].sum + t[t[o].rs].sum;
t[o].mx = max(t[t[o].ls].mx, t[t[o].rs].mx);
}
void modify(int &o, int l, int r, int p) {
if (!o) o = ++num;
if (l == r) {
t[o].sum = t[o].mx = w[rk[p]];
return;
}
int mid = l + r >> 1;
if (p <= mid) modify(t[o].ls, l, mid, p);
else modify(t[o].rs, mid + 1, r, p);
pushup(o);
}
void del(int o, int l, int r, int p) {
if (l == r) {
t[o].sum = t[o].mx = 0;
return;
}
int mid = l + r >> 1;
if (p <= mid) del(t[o].ls, l, mid, p);
else del(t[o].rs, mid + 1, r, p);
pushup(o);
}
int QuerySum(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return t[o].sum;
int mid = l + r >> 1, ret = 0;
if (ql <= mid) ret += QuerySum(t[o].ls, l, mid, ql, qr);
if (qr > mid) ret += QuerySum(t[o].rs, mid + 1, r, ql, qr);
return ret;
}
int QueryMax(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return t[o].mx;
int mid = l + r >> 1, ret = 0;
if (ql <= mid) chkmax(ret, QueryMax(t[o].ls, l, mid, ql, qr));
if (qr > mid) chkmax(ret, QueryMax(t[o].rs, mid + 1, r, ql, qr));
return ret;
}
int GetSum(int x, int y) {
int tx = top[x], ty = top[y], ret = 0, cx = c[x], cy = c[y];
while (tx != ty) {
if (dep[tx] >= dep[ty])
ret += QuerySum(rt[cx], 1, n, dfn[tx], dfn[x]), x = fa[tx];
else
ret += QuerySum(rt[cy], 1, n, dfn[ty], dfn[y]), y = fa[ty];
tx = top[x], ty = top[y];
}
if (dfn[x] < dfn[y]) ret += QuerySum(rt[cx], 1, n, dfn[x], dfn[y]);
else ret += QuerySum(rt[cx], 1, n, dfn[y], dfn[x]);
return ret;
}
int GetMax(int x, int y) {
int tx = top[x], ty = top[y], ret = 0, cx = c[x], cy = c[y];
while (tx != ty) {
if (dep[tx] >= dep[ty])
chkmax(ret, QueryMax(rt[cx], 1, n, dfn[tx], dfn[x])), x = fa[tx];
else
chkmax(ret, QueryMax(rt[cy], 1, n, dfn[ty], dfn[y])), y = fa[ty];
tx = top[x], ty = top[y];
}
if (dfn[x] < dfn[y]) chkmax(ret, QueryMax(rt[cx], 1, n, dfn[x], dfn[y]));
else chkmax(ret, QueryMax(rt[cx], 1, n, dfn[y], dfn[x]));
return ret;
}
int main() {
int u, v;
char op[5];
n = gi(), q = gi();
for (int i = 1; i <= n; ++i) w[i] = gi(), c[i] = gi();
for (int i = 1; i < n; ++i) {
u = gi(), v = gi();
insert(u, v), insert(v, u);
}
dfs1(1, 0), dfs2(1, 1);
for (int i = 1; i <= n; ++i) modify(rt[c[rk[i]]], 1, n, i);
for (int i = 1; i <= q; ++i) {
scanf("%s", op);
u = gi(), v = gi();
if (op[1] == 'C') {
del(rt[c[u]], 1, n, dfn[u]);
c[u] = v;
modify(rt[v], 1, n, dfn[u]);
}
else if (op[1] == 'W') {
w[u] = v;
modify(rt[c[u]], 1, n, dfn[u]);
}
else if (op[1] == 'S') printf("%d\n", GetSum(u, v));
else printf("%d\n", GetMax(u, v));
}
return 0;
}
以下是我因为 \(strcmp\) 导致的 MLE 而对着题解改的空间占用更少,加了一点卡常的代码
LOJ上的码风真好看