题解:P15947 [JOI Final 2026] 集邮 5 / Collecting Stamps 5
_IceCream_ · · 题解
为啥题解区一堆
Content
有一棵
Solution
看到
然后这个至少一个点的限制有点奇怪,我们可以反着做,大条件仍然是
然后我们把它放到点分治下讨论,对于经过根
Case 1 u 到 rt
如果点
这里我们可以对
所以我们记
那么
Case 2 rt 到 v
如果点
同样的,我们对
我们综合起来,我们要统计的一个过
我们发现,后两个条件跟
走到这一步,实际上这题可以用一个二维偏序做,复杂度是
我们对
然后是统计
记得对子树做容斥,因为我们上述提到的所有思路都没有排除
复杂度是优美的
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
bool ST;
const int N = 4e5 + 5;
int n, D, a[N];
struct Edge { int v, nxt; } e[N << 1];
int ecnt, head[N];
void addEdge (int u, int v) {
e[++ecnt] = {v, head[u]};
head[u] = ecnt;
}
int siz[N], rt, dp[N], S;
bool vis[N];
void getroot (int u, int fa) {
siz[u] = 1, dp[u] = 0;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa || vis[v]) continue;
getroot (v, u);
siz[u] += siz[v];
dp[u] = max (dp[u], siz[v]);
}
dp[u] = max (dp[u], S - siz[u]);
if (dp[u] < dp[rt]) rt = u;
}
int dep[N], b[N], c[N], l[N];
int mxd, tot, dot[N];
void getinfo (int u, int fa) {
dot[++tot] = u;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa || vis[v]) continue;
dep[v] = dep[u] + 1; mxd = max (mxd, dep[v]);
b[v] = min (b[u], a[v] + dep[v]);
c[v] = min (c[u], a[v] - dep[v]);
l[v] = min (c[v] - 1, D - dep[v]);
getinfo (v, u);
}
}
int cnt[N], cntd[N], bad[N], totl[N];
// 我们这里可以封装一下,opt = -1 就是对子树做容斥。
void calc (int u, int opt) {
tot = 0;
mxd = dep[u];
if (opt != -1) {
// 特别注意这里对子树做容斥的时候,我们要用的是以 u 为根的信息,所以这里不能重复统计以 v 为根的信息
dep[u] = 0;
b[u] = a[u] + dep[u];
c[u] = a[u] - dep[u];
l[u] = min (c[u] - 1, D - dep[u]);
}
getinfo (u, 0);
for (int i = 1; i <= tot; ++i) (l[dot[i]] >= 0) && (cnt[min (mxd, l[dot[i]])]++), cntd[dep[dot[i]]]++;
for (int i = mxd - 1; i >= 0; --i) cnt[i] += cnt[i + 1];
for (int i = 1; i <= mxd; ++i) cntd[i] += cntd[i - 1];
for (int i = 1; i <= tot; ++i) {
if (dep[dot[i]] < b[dot[i]]) bad[dot[i]] += opt * cnt[dep[dot[i]]];
if (D - dep[dot[i]] >= 0) totl[dot[i]] += opt * cntd[min (mxd, D - dep[dot[i]])];
}
for (int i = 0; i <= mxd; ++i) cnt[i] = cntd[i] = 0;
}
void solve (int u) {
vis[u] = true;
calc (u, 1);
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (vis[v]) continue;
calc (v, -1);
}
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (vis[v]) continue;
S = siz[v], rt = 0; dp[0] = n;
getroot (v, u);
solve (rt);
}
}
bool ED;
int main () {
ios::sync_with_stdio (false);
cin.tie (0), cout.tie (0);
cerr << "[Memory] " << (&ST - &ED) / 1024 / 1024 << " MB\n";
// freopen (".in", "r", stdin);
// freopen (".out", "w", stdout);
cin >> n >> D;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
addEdge (u, v);
addEdge (v, u);
}
S = dp[0] = n, rt = 0;
getroot (1, 0);
solve (rt);
for (int i = 1; i <= n; ++i) cout << totl[i] - bad[i] << '\n';
return 0;
}
::::