题意:给你一些点,每个点都有一定的权值,再给你一些点之间的连接信息,一个点可以跳到另一个点当且仅当终点的权值是途经的所有点中(包括起点)最大的点,刚看到这个题的时候第一想法就是暴力枚举每一个点,dfs遍历所连接的所有点,但看到数据量后果断放弃,暴力必T,然后就开始考虑优化,优化思路就是,用并查集来维护2个点之间的连接和大小关系,具体操作就是先按照点权从小到大将节点排个序,然后枚举每一个节点,找到它连接的所有点,如果它所连接的点的点权比当前点小的话,先找到当前点的父亲节点X,再建立一条从当前点到X的边,并将当前点设为X点的父亲节点,这里有人可能就会有疑问了,X点有没有可能比当前节点要大呀,那不就错了吗?,注意这里我们是按权值从小到大对节点进行枚举的,X点我们在之前必然已经遍历过了,所以它必然比现在我们枚举的节点要小,这样一来我们的思路就非常完美了,那最后的答案又是啥来?,通过前面的操作,我们已经建立了一颗新的树,每个节点在树中的深度就是我们要求的答案啦,建议可以按照刚刚的思路手动模拟一下第2个样例能够帮助更好的理解。
又到了激动人心的环节,上代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N =1e5+10;
int p[N],a[N],b[N],h[N],e[N<<2],ne[N<<2],h1[N],idx,ans[N];
int n;
bool cmp(int x,int y)
{
return a[x]<a[y];
}
void add(int h[],int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
void dfs(int u,int x)
{
ans[u]=x;
for(int i=h1[u];i!=-1;i=ne[i])
{
int j=e[i];
dfs(j,x+1);
}
return ;
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(h,-1,sizeof h);
memset(h1,-1,sizeof h1);
idx=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
p[i]=i;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(h,a,b);
add(h,b,a);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=i;
}
sort(b+1,b+1+n,cmp);
for(int i=1;i<=n;i++)
{
int x=b[i];
for(int k=h[x];k!=-1;k=ne[k])
{
int j=e[k];
if(a[j]<a[x])
{
j=find(j);
p[j]=x;
add(h1,x,j);
}
}
}
dfs(b[n],1);
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
}
return 0;
}