题目
类型:树
解题思路
1、假定该树为多叉树。
2、题目给定的 parents 数组仅支持快速查找某个节点的父节点,为了方便遍历整棵树,先使用「邻接表」将图(树)建起来。
使用 DFS 预处理出 f 数组,其中 f[i] 代表以节点 i 为根节点的子树所包含的节点数量。
3、如何计算「删除某个节点 x 后,剩余连通块的数量,以及每个连通块的节点数量」,根据节点 x 是否为根节点进行分情况讨论:
- 若 x 为根节点,删除后的连通块的数量为「x 的出边数量」,假定共有 k 条出边,根据题目定义,对应的 大小 为各个连通块的节点数量乘积:
- 若 x 不是根节点,删除后的连通块的数量为「x 的出边数量 + 1」,其中 1 代指「以 x 节点的父节点所在的整体连通块」。
假定节点 x 共有 k 条出边,根据题目定义,对应的 大小 为「(各个连通块的节点数量乘积) (x 节点的父节点所在的整体连通块大小)」,而「x 节点的父节点所在的整体连通块大小」,利用容斥原理可知为
f[root] - f[u] = n - f[u],含义为「从原树中减掉以节点 x 为根节点的子树」的部分,即最终 大小 为:
代码
public class CountNodesWithTheHighestScore {static int N = 100010, M = N * 2;static int[] he = new int[N], e = new int[M], ne = new int[M];static int[] f = new int[N];int idx; //成员变量idx 默认初始值0,对边进行编号//建邻接图void add(int a, int b) {e[idx] = b; //当前idx边指向的节点ne[idx] = he[a]; //下一条边 若为-1则表示没有下一个了he[a] = idx++; //头结点}public int countHighestScoreNodes(int[] parents) {Arrays.fill(he, -1); //初始化让所有边都没有下一条int n = parents.length; //节点数目for (int i = 1; i < n; i++) add(parents[i], i); //建图dfs(0);//从根节点开始dfs,在搜索过程中存储当前节点的子树节点数目(含当前节点)long max = 0; //最坑的一个点,使用int会溢出,必须使用longint ans = 0; //最高得分计数for (int x = 0; x < n; x++) {long cur = 1;//删除节点xfor (int i = he[x]; i != -1; i = ne[i]) cur *= f[e[i]]; //累乘删除的左右子树数量if (x != 0) cur *= n - f[x]; //如果不是根节点,还要再把上方剩余子树的n - f[x]个节点数量相乘if (cur > max) {//比之前的乘积大就替换,并把数量置1max = cur; ans = 1;} else if (cur == max) {//跟之前的最大值一样大,则数量+1ans++;}}return ans;}//递归函数int dfs(int u) {int ans = 1;//因为是依据边访问点,说明有一定有一个节点,所以至少都是1for (int i = he[u]; i != -1; i = ne[i]) ans += dfs(e[i]);//把子树的数目加上来f[u] = ans;//存储以u为根节点的树的节点数目return ans;}}
