首页 > 其他分享 >P1751 贪吃虫 题解

P1751 贪吃虫 题解

时间:2023-06-08 10:11:46浏览次数:60  
标签:5001 int 题解 P1751 食物 now 节点 贪吃

题意:

题目传送门

  • 在一棵 n 个结点的树上,有 k 个贪吃虫去吃食物。
  • 每个贪吃虫都走到达食物的唯一路径。
  • 当一条贪吃虫通向食物的道路上有另一条贪吃虫,则较远的那只停止移动。
  • 多条贪吃虫要进入同一节点时,编号最小的才能进入,其他的停止移动。
  • 贪吃虫的移动速度皆为 1。一只贪吃虫吃到食物后,另一个食物立刻出现在树上,贪吃虫继续按以上规则移动。
  • 最后求出每条贪吃虫所在的位置与吃到食物的数量。

思路:

可以把每一个食物看成一个测试点。那么就是要计算出每个节点被哪条贪吃虫占领和每条贪吃虫最终停留的地方。

很明显,对于这两个要计算的值,可以通过两次 dfs 来求。

第一次 dfs。对于每一个节点,占领它的贪吃虫一定是到达用时最短的那个。到达它的最短的时间就是最短的子节点到达时间 +1。还要注意两种特殊情况,在代码里也会有说明:

  1. 该点已有贪吃虫。

  2. 有速度相同,取编号最小的贪吃虫。

结束后把占领食物所在节点的贪吃虫吃到的食物数 +1。

第二次 dfs 时,可以利用第一次求出的值取计算。因为在第一次 dfs 时,我们求出了占领每个节点所用的时间,所以可以用一个数组去记录每一条贪吃虫到达最终落脚点的时间,这个值是可以算出的。如果对于某个节点,占据他的贪吃虫到达最终落脚点的时间 = 该节点被占领的时间,则该节点为这条贪吃虫的最终落脚点。

代码:

请勿抄袭。

#include<bits/stdc++.h>
using namespace std;
int n,k,h;
//n,k,h与题中含义一致 
struct stu{
	int nxt,to;
}e[10010];
int cnt;
int head[10010];
//以上为链式前向星存图 
int b[5001];//每一条贪吃虫所在节点 
int p[5001];//每个节点上为哪条贪吃虫,0表示没有贪吃虫 
int c[501];//每一次食物出现的地方 
int eat[5001];//每条贪吃虫吃的食物数 
int t[5001];//占领每个节点的时间 
int o[5001];//每个节点被占据的贪吃虫编号 
int f[5001];//题解中第二次搜索用来记录的数组 
inline void add(int x,int y)//建边 
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
inline void dfs1(int now,int fa)//第一次搜索,求出每个节点被哪条贪吃虫占据 
//now为当前搜索到的节点,fa为上一次搜索的节点,避免重复搜索 
{
	int mp,mt;//记录占领该节点的时间与贪吃虫编号 
	if(p[now])//该点上已有贪吃虫 
	{
		mp=p[now];//占领的贪吃虫即为该节点上的贪吃虫 
		mt=0;//占领速度为0 
	}
	else //否则因为要取最小值,所以设一个大的数 
	{
		mp=9999;
		mt=9999;
	}
	for(int i=head[now];i;i=e[i].nxt)//依次搜索每一个儿子 
	{
		int to=e[i].to;
		if(to==fa) continue;//避免重复搜索 
		dfs1(to,now);
		if((t[to]+1)<mt||((t[to]+1)==mt&&o[to]<mp))
		//时间更短或时间相同并且编号较小即可更新 
		{
			mt=t[to]+1;
			mp=o[to];
		}
	}
	t[now]=mt;
	o[now]=mp;
	//保存计算后的值 
}
inline void dfs2(int now,int fa)//第二次搜索,计算每一只贪吃虫最终落脚点 
//参数意义与dfs1一致 
{
	if(o[now]!=9999)//=9999说明没有贪吃虫来过,没必要计算 
	{
		if(f[o[now]]==-1&&o[fa]!=o[now])
		//没有计算过,后面的条件是因为贪吃虫一样,那么f[o[now]]没计算过,f[o[fa]]也一定没计算过 
		{   //计算,取三种可能时间的最小值 
			int mt=min(t[fa],t[now]);
			f[o[now]]=min(f[o[fa]],mt);
		}
		if(f[o[now]]!=-1&&f[o[now]]==t[now]) b[o[now]]=now;//计算过且时间一致,则该点为这条贪吃虫的最终落脚点 
	}
	for(int i=head[now];i;i=e[i].nxt)//继续搜索子节点 
	{
		int to=e[i].to;
		if(to==fa) continue;//同理,防重 
		dfs2(to,now);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)//读入这棵树,并建树(边) 
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);//建双向边 
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++)//记录每一条贪吃虫的位置 
	{
		int x;
		scanf("%d",&x);
		p[x]=i;
		b[i]=x;//按照数组含义记录 
	}
	scanf("%d",&h);
	for(int i=1;i<=h;i++)//读入每一次食物出现的位置 
	{
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=h;i++)
	{
		memset(t,0,sizeof(t));
		memset(o,0,sizeof(o));
		memset(f,-1,sizeof(f));//由于数组内还有上一次循环的值,因此将其初始化 
		dfs1(c[i],-1);//搜索 
		++eat[o[c[i]]];//将吃到食物的贪吃虫更新 
		f[o[c[i]]]=t[c[i]];//吃到食物的贪吃虫最终落脚点即为食物处
						   //因此到达最终落脚点的位置即为到达食物的位置
						   //这里也是为了下面的搜索的计算 
		dfs2(c[i],-1);
		memset(p,0,sizeof(p));//由于这一轮过后贪吃虫又有了新的位置,因此先将旧的清零,并更新 
		for(int j=1;j<=k;j++)
		{
			p[b[j]]=j;
		}
	}
	for(int i=1;i<=k;i++)//输出答案 
	{
		printf("%d %d\n",b[i],eat[i]);
	}
	return 0;
}

写题解不易,点个赞呗。

标签:5001,int,题解,P1751,食物,now,节点,贪吃
From: https://www.cnblogs.com/zhangxiao666qwq/p/luogu-P1751.html

相关文章

  • 【题解】 P2221 [HAOI2012]高速公路
    传送门输入时将\(r\)先减1。发现收费之和为$$ans=\sum\limits_{i=l}^{r}a_i\times(r-l+1)\times(i-l+1)$$化简得\[ans=\sum\limits_{i=l}^{r}a_i\times(-i^2+i\times(r+l)+(r-r\timesl-l+1))\]发现可以用线段树来维护:\(s......
  • Codeforces Round 876 (Div. 2) 题解 A - D
    A.TheGoodArray题目大意给定两个整数\(n\)和\(k\),规定一个\(01\)数列为好的的条件是,对于\(1\simn\)中任意的\(i\),都有:\(a\)的前\(i\)个元素中至少有\(\lceil\frac{i}k\rceil\)个都是\(1\)。\(a\)的后\(i\)个元素中至少有\(\lceil\frac{i}k\rceil\)个都是......
  • 题解:【ARC142D】 Deterministic Placing
    题目链接大佬讲解的太精简了,做点蒟蒻视角的思考补充。下面记摆放棋子的点为黑点,没有摆放棋子的为白点。因为进行无数次操作后,占据节点集合总是唯一,所以黑点一定是在反复横跳;每个位置上只能存在一个黑点,所以每次把移动黑点经过的边拿出来,得到的是若干条不相交的链,并且这些链一定......
  • ABC300F 题解
    前两天忘发出来了,补一下QAQ题目链接题意简述给定一个长度为\(n\)且只包含\(\texttt{o}\)和\(\texttt{x}\)的字符串\(s\)以及正整数\(n\)\(m\)\(k\),令字符串\(T=s^{m}\),求将\(T\)中的\(k\)个\(\texttt{x}\)变成\(\texttt{o}\)之后,\(T\)中连续\(\texttt{o}......
  • P3392 涂国旗 题解
    题目大意题目真的是不说人话......有一个国家的国旗是由一个N*M的方格组成的。如果想要这面国旗合法,就必须满足要求:国旗从上到下必须是白色、蓝色和红色,顺序不能改变。每一种颜色都至少有一行。小a这时候捡到了一块破布,希望你通过涂颜色的方式,把破布成合法的国旗,并且要......
  • 题解 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    传送门如题目所言,这就是个线段树合并的板子题。题目大意题目描述首先村落里的一共有\(n\)座房屋,并形成一个树状结构。然后救济粮分\(m\)次发放,每次选择两个房屋\((x,y)\),然后对于\(x\)到\(y\)的路径上(含\(x\)和\(y\))每座房子里发放一袋\(z\)类型的救济粮。然......
  • 【每日一题】LeetCode 786. 第K个最小的素数分数(待补全题解思路)
    题目给你一个按递增顺序排序的数组arr和一个整数k。数组arr由1和若干素数组成,且其中所有整数互不相同。对于每对满足0<i<j<arr.length的i和j,可以得到分数arr[i]/arr[j]。那么第k个最小的分数是多少呢?以长度为2的整数数组返回你的答案,这里answer......
  • 2023上半年(下午)网络工程师试题解析
    2023上半年(下午)网络工程师试题解析试题一(20分)某企业办公楼网络拓扑如图1-1所示。该网络中交换机Switch1-Switch4均是二层设备,分布在办公楼的各层,上联采用千兆光纤。核心交换机、防火墙、服务器部署在数据机房,其中核心交换机实现冗余配置。图1-1问题1(4分)该企业办公网络采用172.16.1......
  • CF1559D2 Mocha and Diana (Hard Version) 题解
    Luogu|Codeforces题意给定两个森林\(A\)和\(B\),均有编号\(1\)到\(n\)的节点,边数分别为\(m_1,m_2\)。现在进行加边操作,但是有两个要求:如果在第一个森林加一条\((u,v)\)的边,第二个森林也要进行同样的操作。反之同理。加边后两个森林依旧是森林。一棵树也是森林。......
  • 应用问题解决-分布式锁(LUA保证删除原子性)
    问题:删除操作缺乏原子性场景1、index1获得锁、执行具体操作、比较lock的uuid值确实和自己生成的uuid是否相等,相等则删除锁。uuid=v1set(lock,uuid)uuid.equals(get("lock"))2、但是index1执行删除前,lock刚好过期时间已经到了,被redis自动释放3、此时index2获取锁,执行具体......