comt oj Comet OJ - Contest #5

迫真小游戏

题目链接:https://cometoj.com/contest/46/problem/C?problem_id=2028

题目描述

 

H君喜欢在阳台晒太阳,闲暇之余他会玩一些塔防小游戏。

H君玩的小游戏可以抽象成一棵 nn 个节点的有根树,树以 11 为根,每个点的深度定义为其到根的简单路径上的点数(根的深度为 11)。

H君有 nn 个干员,H君会按照某种顺序把她们部署到树的每一个节点上,使得每个节点上恰好有一个干员。由于游戏的机制,他们对每个节点 ii 都给出了个限制参数 a_iai​ ,要求H君在第 ii 个节点部署干员之前,所有深度 > a_i>ai​ 的节点上不能有干员。同时游戏为了让玩家过关,保证了 a_iai​ 大于等于点 ii 的深度。

H君将每一次部署干员的节点按顺序写在纸上,形成了一个 1 \dots n1…n 的排列,H君为了获得更多的奖励,想要最小化这个排列的字典序。

我们认为排列 c_1,c_2..c_nc1​,c2​..cn​ 的字典序比排列 d_1,d_2..d_nd1​,d2​..dn​ 的字典序小,当且仅当 c, dc,d 不完全相同且存在一个下标 ii,满足 c_i < d_ici​<di​ 且对于所有 1 \le j < i1≤j<i 的 jj 都有 c_j = d_jcj​=dj​ 。

输入描述

 

第一行一个数 nn 。

接下来 n - 1n−1 行,每行两个数 x, yx,y 表示树上的一条边 。

最后一行 nn 个数,表示 a_iai​ 。

 

数据范围:

1\le n \le 5 \times 10^5, 1 \le a_i \le n1≤n≤5×105,1≤ai​≤n。

输出描述

 

第一行 nn 个数,表示字典序最小的排列。

样例输入 1 

5
1 5
5 3
1 4
4 2
1 3 3 3 2

样例输出 1

1 4 5 2 3

样例输入 2 

10
1 7
7 8
7 2
8 9
7 6
2 4
9 5
8 10
6 3
5 3 4 4 5 3 5 3 5 4

题目是中文题,题意就不描述了

思路:先dfs一遍树把所有节点的高度给记录下来,然后再根据题意维护一个最小堆即可,具体看代码。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define LL long long 

using namespace std;
const int maxn=5e5+100;
vector<int>mp[maxn];
int d[maxn];
int a[maxn];
vector<int>de[maxn];
int Maxlen=-1;
void dfs(int u,int fa,int len)
{
	de[len].push_back(u);
	Maxlen=max(len,Maxlen);
	for(int i=0;i<mp[u].size();i++)
	{
		int v=mp[u][i];
		if(v==fa)
		{
			continue;
		}
		dfs(v,u,len+1);
	}
	return ;
}
int main()
{
	int n;
	scanf("%d",&n);
	
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		mp[u].push_back(v);
		mp[v].push_back(u);	
	}
	for(int i=1;i<=n;i++)
	{
		int gg;
		scanf("%d",&gg);
		d[gg+1]++;
		a[i]=gg+1;
	}
	dfs(1,0,1);	
	priority_queue<int,vector<int>,greater<int> >Q;
	Q.push(1);
	int tt=2;
	int p=1;
	vector<int>ans;
	while(!Q.empty())
	{
		p=Q.top();
		Q.pop();
		d[a[p]]--;
		ans.push_back(p);
		while(tt<=Maxlen&&d[tt]==0)
		{
			for(int i=0;i<de[tt].size();i++)
			{
				p=de[tt][i];
				Q.push(p);
			}
			tt++;
		}
		
	}
	int si=ans.size();
	for(int i=0;i<si;i++)
	{
		if(i!=si-1)
		{
			printf("%d ",ans[i]);
		}
		else
		{
			printf("%d\n",ans[i]);
		}

	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值