2872: 可以被K整除连通块的最大数目
**"无向树"**是图论里的一个基本概念,它同时满足下面两条:
-
它是一张无向图------任意一条边都没有方向,可以"双向走";
-
它是一棵树------连通而且没有圈(cycle)
思路:连通块的数目等于删除的边数加一。
什么样的边可以删除?
如果 x 和 y 都是 k 的倍数,那么 x+y 也是 k 的倍数。比如 3 和 6 都是 3 的倍数,那么 3+6=9 也是 3 的倍数。
反过来说(逆否命题),如果 x+y 不是 k 的倍数,那么 x 和 y 不全是 k 的倍数。++不是 k 的倍数的数,继续拆分,始终存在一个不是 k 的倍数的数。++
对应到删边上,删除一条边后,我们把一个连通块分成了两个连通块。如果其中一个连通块的点权和不是 k 的倍数,那么这个连通块无论如何分割,始终存在一个点权和不是 k 的倍数的连通块。所以当且仅当这两个连通块的点权和都是 k 的倍数,这条边才能删除。
删除后,由于分割出的连通块点权和仍然是 k 的倍数,所以可以继续分割,直到无法分割为止。换句话说,只要有能删除的边,就删除。
如何找到可以删除的边?
删除一条边后,我们把一个连通块分成了两个连通块。由于题目保证整棵树的点权和是 k 的倍数,所以只需看其中一个连通块的点权和是否为 k 的倍数。
从任意点出发 DFS 这棵树。计算子树 x 的点权和 s,如果 s 是 k 的倍数,那么可以删除 x 到其父节点这条边。注意根节点没有父节点。
连通块的数目等于删除的边数加一。++可以把根节点到其父节点这条边(虽然不存在)也算上,这样答案就是删除的边数。++
class Solution {
public:
int maxKDivisibleComponents(int n, vector<vector<int>>& edges, vector<int>& values, int k) {
vector<vector<int>> g(n);
for(auto& e :edges){
int x=e[0],y=e[1];
g[x].push_back(y); //记录每个节点的"邻居"
g[y].push_back(x);
}
int ans=0;
// 返回子树 x 的点权和
auto dfs=[&](this auto&& dfs,int x,int fa)->long long{
long long sum=values[x];
for(int y:g[x]){
if(y!=fa){ // 避免访问父节点,防止走回头路
// 加上子树 y 的点权和,得到子树 x 的点权和
sum+=dfs(y,x);
}
}
ans+=(sum%k==0);
return sum;
};
dfs(0,-1);
return ans;
}
};