这道题开始先直接dfs,然后tle了,然后换汤不换药bfs一遍,又tle了。看了别人的思路,恍然大悟。
TLE思路:建图,然后对于每次轰炸,以轰炸的节点为根进行dfs,更新相邻节点损毁的次数。
TLE写起来简单粗暴,但结果也很粗暴。。。
AC思路:建图后先进行一次dfs(以编号为1的节点为根,将无根树转为有根树)找出每个节点对应的父节点,然后根据父子关系,爷孙关系以及兄弟关系,推出节点的损毁次数。(兄弟被轰炸产生的损毁较容易i被漏掉)
两种思路对比以下,第二种思路确实剪枝了许多,因为需要查询的只有被轰炸的点,所以每次直接直接计算出该点的次数即可,无需全部点更新
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=1e6+50;
int n,q,u,v,boom1[maxn],boom2[maxn],boom3[maxn],boom[maxn],pa[maxn];
///boom1[x]表示的是 炸x点时 直接 波及到的爸爸节点,
///boom2则表示 直接 波及到的爷爷节点,
///boom3表示波及到的兄弟节点,记录在父节点上,
///boom表示这个点直接被炸的次数
vector<int> node[maxn];
void dfs(int id,int fa){
for(int i=0;i<node[id].size();i++){
int son=node[id][i];
if(son==fa) continue;
pa[son]=id;
dfs(son,id);
}
}
int main(){
while(scanf("%d%d",&n,&q)!=EOF){
memset(boom,0,sizeof(boom));
memset(boom1,0,sizeof(boom1));
memset(boom2,0,sizeof(boom2));
memset(boom3,0,sizeof(boom3));
memset(node,0,sizeof(node));
memset(pa,0,sizeof(pa));
for(int i=0;i<n-1;i++){
scanf("%d%d",&u,&v);
node[u].push_back(v);
node[v].push_back(u);
}
dfs(1,0);///递归得出每个点的父节点
for(int i=0;i<q;i++){
int temp;
scanf("%d",&temp);
boom[temp]++;
boom1[pa[temp]]++;
boom2[pa[pa[temp]]]++;
boom3[pa[temp]]++;///对兄弟节点的影响直接记录在父节点上,且这样做还能把自己点上的也算上
printf("%d\n",boom1[temp]+boom2[temp]+boom[pa[temp]]+boom[pa[pa[temp]]]+boom3[pa[temp]]);
}
}
return 0;
}