-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
2867. Count Valid Paths in a Tree
参考:
- [2867. 统计树中的合法路径数目](https://leetcode.cn/problems/count-valid-paths-in-a-tree/description/?envType=daily-question&envId=2024-02-27)
这道题难点在于如何用乘法原理计算每个路径的个数。我一开始打算用后序遍历来做,但后序遍历用乘法无法计算一条直路径的满足条件个数,所以按照正常逻辑应该用类似染色的思想来做,也就是如下的以质数节点为中心dfs来做。
乘法原理:循环累加过程中,可以用过当前个数curCnt * preSum来累计计算路径。
贴一个灵神的做法:
class Solution {
private final static int MX = (int) 1e5;
private final static boolean[] np = new boolean[MX + 1]; // 质数=false 非质数=true
static {
np[1] = true;
for (int i = 2; i * i <= MX; i++) {
if (!np[i]) {
for (int j = i * i; j <= MX; j += i) {
np[j] = true;
}
}
}
}
public long countPaths(int n, int[][] edges) {
List<Integer>[] g = new ArrayList[n + 1];
Arrays.setAll(g, e -> new ArrayList<>());
for (var e : edges) {
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
long ans = 0;
int[] size = new int[n + 1];
var nodes = new ArrayList<Integer>();
for (int x = 1; x <= n; x++) {
if (np[x]) { // 跳过非质数
continue;
}
int sum = 0;
for (int y : g[x]) { // 质数 x 把这棵树分成了若干个连通块
if (!np[y]) {
continue;
}
if (size[y] == 0) { // 尚未计算过
nodes.clear();
dfs(y, -1, g, nodes); // 遍历 y 所在连通块,在不经过质数的前提下,统计有多少个非质数
for (int z : nodes) {
size[z] = nodes.size();
}
}
// 这 size[y] 个非质数与之前遍历到的 sum 个非质数,两两之间的路径只包含质数 x
ans += (long) size[y] * sum;
sum += size[y];
}
ans += sum; // 从 x 出发的路径
}
return ans;
}
private void dfs(int x, int fa, List<Integer>[] g, List<Integer> nodes) {
nodes.add(x);
for (int y : g[x]) {
if (y != fa && np[y]) {
dfs(y, x, g, nodes);
}
}
}
}
那么,根据这种染色的思想,可以用并查集来做,因为连通块之间都是被质数节点割裂开的,所以我们可以union两个非质数节点,然后再遍历质数节点用乘法原理计算。
class PrimeTable {
private final boolean[] prime;
public PrimeTable(int n) {
prime = new boolean[n + 1];
Arrays.fill(prime, true);
prime[0] = false;
prime[1] = false;
for (int i = 2; i <= n; ++i) {
if (prime[i]) {
for (int j = i + i; j <= n; j += i) {
prime[j] = false;
}
}
}
}
public boolean isPrime(int x) {
return prime[x];
}
}
class UnionFind {
private final int[] p;
private final int[] size;
public UnionFind(int n) {
p = new int[n];
size = new int[n];
for (int i = 0; i < n; ++i) {
p[i] = i;
size[i] = 1;
}
}
public int find(int x) {
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
public boolean union(int a, int b) {
int pa = find(a), pb = find(b);
if (pa == pb) {
return false;
}
if (size[pa] > size[pb]) {
p[pb] = pa;
size[pa] += size[pb];
} else {
p[pa] = pb;
size[pb] += size[pa];
}
return true;
}
public int size(int x) {
return size[find(x)];
}
}
class Solution {
private static final PrimeTable PT = new PrimeTable(100010);
public long countPaths(int n, int[][] edges) {
List<Integer>[] g = new List[n + 1];
Arrays.setAll(g, i -> new ArrayList<>());
UnionFind uf = new UnionFind(n + 1);
for (int[] e : edges) {
int u = e[0], v = e[1];
g[u].add(v);
g[v].add(u);
if (!PT.isPrime(u) && !PT.isPrime(v)) {
uf.union(u, v);
}
}
long ans = 0;
for (int i = 1; i <= n; ++i) {
if (PT.isPrime(i)) {
long t = 0;
for (int j : g[i]) {
if (!PT.isPrime(j)) {
long cnt = uf.size(j);
ans += cnt;
ans += cnt * t;
t += cnt;
}
}
}
}
return ans;
}
}
Metadata
Metadata
Assignees
Labels
No labels