P4242 树上的毒瘤 题解
树上问题全家桶。
以给出的点集建出虚树,然后问题转化为对每个关键点,求其到其他关键点的虚树链上颜色段数之和。
注意虚树的一条边是原树的一条链,所以虚树某条链上的颜色段数并不是简单的虚树上这些点形成的颜色段数。
所以令虚树的边权为其在原树上对应的链上的颜色段数
需要维护链推平,建虚树时需要查询链上颜色段数
然后问题转化为对每个关键点,求其到其他关键点的虚树链边权和
#include <cstdio>
#include <algorithm>
using namespace std;
struct H
{
struct E
{
int v, w, t;
} e[200050];
int c, h[100050];
void A(int u, int v, int w)
{
e[++c] = {v, w, h[u]};
h[u] = c;
}
} T, V;
bool e[100050], E[100050];
long long A, G[100050];
int n, m, p, o, r, l, w[100050], g[100050], h[100050], a[100050], z[100050], d[100050], f[100050],
s[100050], t[100050], b[100050], k[100050], S[100050], P[100050], Z[100050], W[100050];
bool C(int x, int y) { return b[x] < b[y]; }
void X(int u)
{
s[u] = 1;
for (int i = T.h[u], v; i; i = T.e[i].t)
if (!d[v = T.e[i].v])
{
d[v] = d[f[v] = u] + 1;
X(v);
s[u] += s[v];
if (s[v] > s[z[u]])
z[u] = v;
}
}
void Y(int u, int g)
{
t[k[b[u] = ++p] = u] = g;
if (z[u])
Y(z[u], g);
for (int i = T.h[u],
v;
i; i = T.e[i].t)
if ((v = T.e[i].v) != f[u] && v != z[u])
Y(v, v);
}
int L(int x, int y)
{
while (t[x] != t[y])
{
if (d[t[x]] < d[t[y]])
swap(x, y);
x = f[t[x]];
}
return d[x] < d[y] ? x : y;
}
struct N
{
int p, q, v;
};
struct O
{
N v;
int z;
} R[100050 << 2];
N operator+(N a, N b) { return {a.p, b.q, a.v + b.v - (a.q == b.p)}; }
void U(int p) { R[p].v = R[p << 1].v + R[p << 1 | 1].v; }
void F(int p, int x)
{
R[p].z = R[p].v.p = R[p].v.q = x;
R[p].v.v = 1;
}
void D(int p)
{
if (~R[p].z)
F(p << 1, R[p].z), F(p << 1 | 1, R[p].z), R[p].z = -1;
}
void B(int s, int t, int p)
{
R[p].z = -1;
if (s == t)
return (void)(R[p].v.p = R[p].v.q = a[k[s]], R[p].v.v = 1);
int m = s + t >> 1;
B(s, m, p << 1);
B(m + 1, t, p << 1 | 1);
U(p);
}
void M(int l, int r, int x, int s, int t, int p)
{
if (l <= s && t <= r)
return F(p, x);
D(p);
int m = s + t >> 1;
if (l <= m)
M(l, r, x, s, m, p << 1);
if (r > m)
M(l, r, x, m + 1, t, p << 1 | 1);
U(p);
}
N Q(int l, int r, int s, int t, int p)
{
if (l <= s && t <= r)
return R[p].v;
D(p);
int m = s + t >> 1;
if (l <= m && r > m)
return Q(l, r, s, m, p << 1) + Q(l, r, m + 1, t, p << 1 | 1);
if (l <= m)
return Q(l, r, s, m, p << 1);
if (r > m)
return Q(l, r, m + 1, t, p << 1 | 1);
}
void M(int u, int v, int x)
{
while (t[u] != t[v])
{
if (d[t[u]] < d[t[v]])
swap(u, v);
M(b[t[u]], b[u], x, 1, n, 1);
u = f[t[u]];
}
if (d[u] > d[v])
swap(u, v);
M(b[u], b[v], x, 1, n, 1);
}
int Q(int u, int v)
{
N p, q;
bool U = 0, V = 0;
while (t[u] != t[v])
if (d[t[u]] > d[t[v]])
p = U ? Q(b[t[u]], b[u], 1, n, 1) + p : (U = 1, Q(b[t[u]], b[u], 1, n, 1)), u = f[t[u]];
else
q = V ? Q(b[t[v]], b[v], 1, n, 1) + q : (V = 1, Q(b[t[v]], b[v], 1, n, 1)), v = f[t[v]];
if (d[u] > d[v])
p = U ? Q(b[v], b[u], 1, n, 1) + p : (U = 1, Q(b[v], b[u], 1, n, 1));
else
q = V ? Q(b[u], b[v], 1, n, 1) + q : (V = 1, Q(b[u], b[v], 1, n, 1));
if (!U)
return q.v;
if (!V)
return p.v;
swap(p.p, p.q);
return (p + q).v;
}
void X(int u, int k, int t)
{
S[u] = 1;
P[u] = 0;
for (int i = V.h[u], v; i; i = V.e[i].t)
if (!E[v = V.e[i].v] && v != k)
X(v, u, t), S[u] += S[v], P[u] = max(P[u], S[v]);
if (P[r] > (P[u] = max(P[u], t - S[u])))
r = u;
}
void Y(int u, int k, int)
{
if (e[u])
Z[l++] = u;
for (int i = V.h[u], v; i; i = V.e[i].t)
if (!E[v = V.e[i].v] && v != k)
W[v] = W[u] + V.e[i].w, Y(v, u, 0);
}
void Q(int u, int k, int)
{
E[u] = 1;
o = e[u];
for (int i = V.h[u], v; i; i = V.e[i].t)
if (!E[v = V.e[i].v] && v != k)
{
W[v = V.e[i].v] = V.e[i].w;
Y(v, u, l = 0);
o += l;
for (int j = 0; j < l; ++j)
A += W[Z[j]];
}
if (e[u])
G[u] += A + o;
for (int i = V.h[u], v; i; i = V.e[i].t)
if (!E[v = V.e[i].v] && v != k)
{
W[v = V.e[i].v] = V.e[i].w;
Y(v, u, l = 0);
o -= l;
for (int j = 0; j < l; ++j)
A -= W[Z[j]];
for (int j = 0; j < l; ++j)
G[Z[j]] += A + o * (W[Z[j]] + 1ll);
o += l;
for (int j = 0; j < l; ++j)
A += W[Z[j]];
}
A = o = 0;
for (int i = V.h[u], v; i; i = V.e[i].t)
if (!E[v = V.e[i].v] && v != k)
r = 0, X(v, u, s[v]), Q(r, u, 0);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i);
for (int i = 1, u, v; i < n; ++i)
scanf("%d%d", &u, &v), T.A(u, v, 1), T.A(v, u, 1);
X(d[1] = 1);
Y(1, 1);
B(1, n, 1);
for (int i = 0, o, k, c = 0, u, v, x; i < m; ++i)
{
scanf("%d", &o);
if (o & 1)
scanf("%d%d%d", &u, &v, &x), M(u, v, x);
else
{
scanf("%d", &k);
for (int j = 0; j < c; ++j)
V.h[h[j]] = e[h[j]] = E[h[j]] = G[h[j]] = 0;
for (int j = V.c = c = 0; j < k; ++j)
scanf("%d", g + j), e[w[j] = g[j]] = 1;
sort(g, g + k, C);
h[c++] = g[0];
for (int j = 1; j < k; ++j)
h[c++] = L(g[j], g[j - 1]), h[c++] = g[j];
sort(h, h + c, C);
c = unique(h, h + c) - h;
for (int j = 1, l; j < c; ++j)
l = L(h[j], h[j - 1]), V.A(l, h[j], Q(l, h[j]) - 1), V.A(h[j], l, Q(l, h[j]) - 1);
P[r = 0] = 1e9;
X(h[0], 0, c);
Q(r, 0, 0);
for (int j = 0; j < k; ++j)
printf("%lld ", G[w[j]]);
puts("");
}
}
return 0;
}
写 + 调全程花费 2h。
题如其名,码量约等于半个猪国杀。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具