思路
题目就不重复贴了,直接梳理一下本题思路:
由题目给出的上图,我们可以看到:根节点root实际上包含了每一个类别,根节点root的次级类别也就是图上除根节点之外的其他节点。那么,对于每一个类别,统计它和其全部后代类别的权重之和,同时统计其余全部类别的权重之和,并求二者差值的绝对值(题目原话),其实就可以简化为:abs((以root为根的子树的权重之和 - 以当前节点为根的子树的权重之和) - 以当前节点为根的子树的权重之和)。这一步可以用dfs做到,时间复杂度为,本题的数据量为2000,因此可以用dfs。
用dfs我们就可以每次找到 最小的存在的节点,作为这一轮中用户的询问 。对于用户的每次询问:(1)如果一个询问 得到的结果为 Yes,那么仅保留以 为根的子树,删除其余部分,即将根节点 rt 赋值为 ;(2)如果得到的结果为 No,那么保留其余部分,删除 以及以 为根的子树,可以将 节点的 vis 赋值为 − 1,在 dfs 过程中遇到 vis 为 − 1的节点直接返回。
最后得到结果,退出循环的可能有两种,正如题目最后给的例子一样:(1)当前求得的w最小值的节点正好是要求的节点,rt == tar,得到结果;(2)遍历完了目标节点的所有子节点,只剩下目标节点时,得到结果。要注意的是,只有最后只剩下一个类别,才可确定名词的类别。
值得一提的是vis数组,它有两个作用:(1)当我们需要删除一个节点时,将vis赋值为 -1,这样,后续再dfs的时候,遇到 vis == -1的节点就可以直接跳过。(2)当我们更新 rt 时,按理来说要将那些不属于 新rt 为根的子树中的节点删去,但是如果在每一次dfs中都给相应的节点vis++,我们就可以通过比较 vis[i] == vis[rt] 来判断,节点 是否在我们新的子树中,因为根节点被访问的次数,一定等于它的有效子节点被访问的次数。这样也就省去了删掉那些节点的步骤。
时间复杂度为
C++代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int N = 2010;
int n,m;
int w[N],fa[N]; // w记录节点权重,fa记录节点的上级节点(类似父节点)
long long wsum[N]; // 记录以当前节点为根的子树的权重之和
vector<vector<int>> g(N); // 记录当前子树的次级类别(类似子节点)
vector<int> vis; // 记录当前节点的访问情况
long long dfs(int u)
{
wsum[u] = w[u];
vis[u]++;
for(auto v : g[u])
{
if(vis[v]!=-1)
wsum[u] += dfs(v);
}
return wsum[u];
}
int main()
{
cin >> n >> m;
for(int i = 1;i<=n;i++)
cin >> w[i];
for(int i=2;i<=n;i++)
{
cin >> fa[i];
g[fa[i]].push_back(i);
}
while(m--)
{
int tar;
cin >> tar;
int rt = 1,cnt = g[tar].size(); // cnt记录目标节点的次级类别个数
vis.assign(n+1,0);
set<int> belong;
for(int cur = tar; cur ; cur = fa[cur]) belong.insert(cur); // 加入当前cur类别所属的所有上级类别
while(cnt || rt!=tar) // cnt==0 表示目标节点的子节点都被删完了,只剩下目标节点了,直接得到答案
{
dfs(rt);
vector<long long> delta(n+1);
for(int i=1;i<=n;i++)
{
delta[i] = abs(wsum[i] - (wsum[rt]-wsum[i]));
}
int idx = rt;
for(int i=1;i<=n;i++) //直接遍历所有节点,只有满足==vis[rt]的才是有效节点
{
if(vis[i] == vis[rt] && delta[i] < delta[idx]) idx = i;
}
cout << idx << ' ';
if(belong.count(idx)) rt = idx; // 将根节点赋值为idx,只遍历以该节点为根的子树
else vis[idx] = -1; // 置为-1表示该节点被删除了
if(fa[idx] == tar) cnt--;
}
cout << endl;
}
return 0;
}
有问题欢迎指出讨论~