牛客算法周周练2 小H和游戏(树+思维)

链接:https://ac.nowcoder.com/acm/contest/5203/D

题目描述

小H正在玩一个战略类游戏,她可以操纵己方的飞机对敌国的N座城市(编号为1~N)进行轰炸
敌国的城市形成了一棵树,小H会依次进行Q次轰炸,每次会选择一个城市A进行轰炸,和这座城市距离不超过2的城市都会受损(这里距离的定义是两点最短路径上的边数),轰炸结束后,小H还想知道当前城市A受损的次数
作为游戏的开发者之一,你有义务回答小H的问题

输入描述:

第1行,两个整数N(1≤N≤750000)、Q(1≤Q≤750000)
第2~N行,每行两个整数表示树上的一条边
第N+1~N+Q行,每行一个整数,表示小H这次轰炸的城市

输出描述:

输出Q行,每行一个整数表示这一次轰炸的城市在此次轰炸后共计受损几次

示例1

输入
4 4
1 2
2 3
3 4
1
2
3
4
输出
1
2
3
3

思路:

  • 算伤害

设结点x,我们可以知道结点x受到伤害==爷爷造成的伤害+父亲+自己+兄弟+儿子+孙子。我们分三步来计算伤害:
1.爷爷和父亲造成的伤害等于在爷爷和父亲处投弹的次数,用一个数组记录每个结点投弹次数就可以求出来了。
2.自己和兄弟的伤害,直接求兄弟不好求,我们可以通过父结点受伤来求。用f[fa[x]][1] (fa[x]是x结点的父亲)数组记录父节点到距离为1的所有子节点造成的伤害,自己和兄弟对自己造成的伤害也就是f[fa[x]][1]的值,每次投弹时f[fa[x]][1]++来维护。
3.儿子和孙子造成的伤害等于儿子和距离为2孙子(孙子找两次父亲)的投弹次数,它是直线传递的,只需要用一个数组记录,每次儿子和孙子投弹的时候+1就可以了。

  • 建树

因为不需要遍历树,只需要模拟树形结构,每次向上传2步值就可以,所以可以不用真的建树。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=8e5;
int fa[maxn],f[maxn][3],a[maxn];
int main()
{
    
    
	int n,q,u,v,x;
	cin>>n>>q;
	for(int i=1;i<=n-1;i++)//建树
	{
    
    
		cin>>u>>v;
		if(!fa[u])
		fa[u]=v;
		else
		fa[v]=u;
	}
	while(q--)
	{
    
    
		int sum;//记录自己和兄弟的伤害 
		cin>>x;
		a[x]++;//自己+1 
		if(fa[x])//父亲存在
		{
    
    
			f[fa[x]][1]++;//一格父亲+1,一格父亲记录的是自己和兄弟的伤害
			f[fa[x]][2]++;//二格父亲+1,二格父亲记录的是孙子和儿子的伤害
			sum=f[fa[x]][1];//自己和兄弟的伤害 
			if(fa[fa[x]])//爷爷存在 
			f[fa[fa[x]]][2]++;//爷爷+1 
		}
		else
		sum=a[x];
		cout<<a[fa[x]]+a[fa[fa[x]]]+sum+f[x][2]<<endl;
		//父亲+爷爷+自己和兄弟+孙子和儿子 
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xiaolan7777777/article/details/105589074