本题可以根据权值最大的点作为突破口,权值最大的点一定是最优解的最后一步,那么我们可以依次倒推,每次删去最大的点以及该点连接的边最后推到每一个点。
实现过程:我们可以将所有点按照权值从小到大排出,每次枚举出的新点因为权值比前面的大,所以可以将他作为原先连通块的新祖宗,这样在搜寻深度倒推的时候就能够实现优先删除一个连通块中权值较大的点,再删除权值较小的点,每次倒推与该点相连的点的时候,与该点相连的点的深度为该点深度加1,直到倒推到无法倒推。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 100010;
typedef pair<int, int>PII;
vector<int> next1[M], next2[M];
PII map1[M];
int pre[M];
bool vis[M];
int len[M];
void init(int n) {
for (int i = 1; i <= n; ++i) {
pre[i] = i;
next1[i].clear();
next2[i].clear();
}
}
int find(int son) {
int deson, dad1;
deson = son;
while(son != pre[son]) son = pre[son];
while(deson != pre[deson]) {
dad1 = pre[deson];
pre[deson] = son;
deson = dad1;
}
return son;
}
void find_depth(int now) {
for (int i : next2[now]) {
len[i] = len[now] + 1;
find_depth(i);
}
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
int n, u, v;
scanf("%d", &n);
init(n);
for (int i = 0; i < n - 1; ++i) {
scanf("%d%d", &u, &v);
next1[u].push_back(v); next1[v].push_back(u);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &map1[i].first);
map1[i].second = i;
}
sort(map1 + 1, map1 + 1 + n); // 按照点权值从小到大排序
memset(vis, false, sizeof(vis));
vis[map1[1].second] = true;
int temp1;
for (int i = 2; i <= n; ++i) {
int temp = map1[i].second;
for (int j : next1[temp]) {
if(!vis[j]) continue;
temp1 = find(j);
pre[temp1] = temp;
next2[temp].push_back(temp1);
}
vis[temp] = 1;
}
memset(len, 0, sizeof(len));
len[map1[n].second] = 1;
find_depth(map1[n].second);
for (int i = 1; i <= n; ++i) printf("%d\n", len[i]);
}
return 0;
}