Jumping Monkey

 

 题意:给你一些点,每个点都有一定的权值,再给你一些点之间的连接信息,一个点可以跳到另一个点当且仅当终点的权值是途经的所有点中(包括起点)最大的点,刚看到这个题的时候第一想法就是暴力枚举每一个点,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;
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值