题解:P15947 [JOI Final 2026] 集邮 5 / Collecting Stamps 5

· · 题解

简洁 \mathcal{O}(n \log n) 做法。

我说很简单但我考上不想写点分治你信吗。

显然点分治。

考虑一条路径 s \longrightarrow rt \longrightarrow t。

其中 rt 是分治中心。

分讨 a_u 在哪边:

因为 u 在 s 往上走的路上,所以耗时是 dep_s - dep_u。

触发条件是 dep_s - dep_u \ge a_u \iff dep_s \ge a_u + dep_u。

限制了这个条件后另一边的 t 任选。

那么触发条件是 $dep_s + dep_u \ge a_u \iff dep_s \ge a_u - dep_u$。 --- 注意到这两部分独立,所以从分治中心往下走处理每个 $s$ 到 $rt$ 的路径上的 $\min(a_u + dep_u)$,能得出这边有没有 $a_u$ 合法。 顺便能处理出 $\min(a_u - dep_u)$ 表示另一边合法的限制。 + 如果 $s$ 上半段已经合法,即要求 $dep_s \in [0, m - dep_t]$。 + 如果 $s$ 上半段没合法,即要求 $dep_s \in [s[t], m - dep_t]$。 对于每个 $t$ 给的区间做差分单点加,因为是深度限制,所以事实上不用前缀和到 $m$,做完了。 ::::success[Code] ```cpp #include <bits/stdc++.h> //#define int long long #define PII pair < int , int > #define x first #define y second using namespace std ; bool bbb ; const int N = 4e5 + 5 , inf = 1e9 ; int n , m , a[N] ; vector < int > g[N] ; int sz[N] , vis[N] ; int dp[N] , rt ; void getsz(int u , int fa){ sz[u] = 1 ; for(int v : g[u]) if(v != fa && !vis[v]) getsz(v , u) , sz[u] += sz[v] ; } void getrt(int u , int fa , int tot){ dp[u] = 0 ; for(int v : g[u]) if(v != fa && !vis[v]) getrt(v , u , tot) , dp[u] = max(dp[u] , sz[v]) ; dp[u] = max(dp[u] , tot - sz[u]) ; if(dp[rt] > dp[u]) rt = u ; } int dep[N] , hf[N] , s[N] ; vector < int > p ; void getd(int u , int fa , int v1 , int v2 , int w){ // v1 : a[u] + dep[u] , v2 : a[u] - dep[u] dep[u] = dep[fa] + 1 ; if(w == 1){ v1 = min(v1 , a[u] + dep[u]) , v2 = min(v2 , max(0 , a[u] - dep[u])) ; hf[u] = v1 <= dep[u] , s[u] = v2 ; } p.push_back(u) ; for(int v : g[u]) if(v != fa && !vis[v]) getd(v , u , v1 , v2 , w) ; } int cf[N][2] ; long long ans[N] ; void calc(int u , int d , int w){ dep[0] = d , getd(u , 0 , inf , inf , w) ; int mx = 0 ; for(int i : p) mx = max(mx , dep[i]) ; int lim = min(m , mx) ; for(int i : p){ int r = min(lim , m - dep[i]) + 1 ; if(m >= dep[i] && r > 0) ++ cf[0][1] , -- cf[r][1] ; if(m - dep[i] >= s[i] && r > s[i]) ++ cf[s[i]][0] , -- cf[r][0] ; } for(int i = 1 ; i <= lim ; i++) cf[i][0] += cf[i - 1][0] , cf[i][1] += cf[i - 1][1] ; for(int i : p) if(dep[i] <= lim) ans[i] += 1ll * cf[dep[i]][hf[i]] * w ; for(int i = 0 ; i <= lim + 1 ; i++) cf[i][0] = cf[i][1] = 0 ; p.clear() ; } void solve(int u){ calc(u , -1 , 1) , vis[u] = 1 ; for(int v : g[u]) if(!vis[v]) calc(v , 0 , -1) , rt = 0 , getsz(v , u) , getrt(v , u , sz[v]) , solve(rt) ; } bool eee ; signed main(){ // freopen("distort.in" , "r" , stdin) ; // freopen("distort.out" , "w" , stdout) ; ios :: sync_with_stdio(0) ; cin.tie(0) ; cout.tie(0) ; cerr << "Memory: " << abs((char*)&eee - (char*)&bbb) / 1024.0 / 1024.0 << " MB\n" ; cin >> n >> m ; for(int i = 1 ; i <= n ; i++) cin >> a[i] ; for(int i = 1 , u , v ; i < n ; i++){ cin >> u >> v ; g[u].push_back(v) , g[v].push_back(u) ; } dp[rt = 0] = inf , getsz(1 , 0) , getrt(1 , 0 , n) , solve(rt) ; for(int i = 1 ; i <= n ; i++) cout << ans[i] << '\n' ; return 0 ; } /* ``` ::::