• Leetcode.2867 统计树中的合法路径数目


    题目链接

    Leetcode.2867 统计树中的合法路径数目 rating : 2428

    题目描述

    给你一棵 n n n 个节点的无向树,节点编号为 1 1 1 n n n 。给你一个整数 n n n 和一个长度为 n − 1 n - 1 n1 的二维整数数组 e d g e s edges edges ,其中 e d g e s [ i ] = [ u i , v i ] edges[i] = [u_i, v_i] edges[i]=[ui,vi] 表示节点 u i u_i ui v i v_i vi 在树中有一条边。

    请你返回树中的 合法路径数目

    如果在节点 a a a 到节点 b b b 之间 恰好有一个 节点的编号是质数,那么我们称路径 ( a , b ) (a, b) (a,b)合法的

    注意:

    • 路径 ( a , b ) (a, b) (a,b) 指的是一条从节点 a a a 开始到节点 b b b 结束的一个节点序列,序列中的节点 互不相同 ,且相邻节点之间在树上有一条边。
    • 路径 ( a , b ) (a, b) (a,b) 和路径 ( b , a ) (b, a) (b,a) 视为 同一条 路径,且只计入答案 一次
    示例 1:

    在这里插入图片描述

    输入:n = 5, edges = [[1,2],[1,3],[2,4],[2,5]]
    输出:4
    解释:恰好有一个质数编号的节点路径有:

    • (1, 2) 因为路径 1 到 2 只包含一个质数 2 。
    • (1, 3) 因为路径 1 到 3 只包含一个质数 3 。
    • (1, 4) 因为路径 1 到 4 只包含一个质数 2 。
    • (2, 4) 因为路径 2 到 4 只包含一个质数 2 。
      只有 4 条合法路径。
    示例 2:

    在这里插入图片描述

    输入:n = 6, edges = [[1,2],[1,3],[2,4],[3,5],[3,6]]
    输出:6
    解释:恰好有一个质数编号的节点路径有:

    • (1, 2) 因为路径 1 到 2 只包含一个质数 2 。
    • (1, 3) 因为路径 1 到 3 只包含一个质数 3 。
    • (1, 4) 因为路径 1 到 4 只包含一个质数 2 。
    • (1, 6) 因为路径 1 到 6 只包含一个质数 3 。
    • (2, 4) 因为路径 2 到 4 只包含一个质数 2 。
    • (3, 6) 因为路径 3 到 6 只包含一个质数 3 。
      只有 6 条合法路径。
    提示:
    • 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105
    • e d g e s . l e n g t h = n − 1 edges.length = n - 1 edges.length=n1
    • e d g e s [ i ] . l e n g t h = 2 edges[i].length =2 edges[i].length=2
    • 1 ≤ u i , v i ≤ n 1 \leq u_i, v_i \leq n 1ui,vin
    • 输入保证 e d g e s edges edges 形成一棵合法的树。

    解法:筛质数 + dfs

    我们先将 [ 1 , 1 0 5 ] [1,10^5] [1,105] 内的质数筛出来,用 n p np np 记录。如果 n p [ x ] = t r u e np[x] = true np[x]=true,说明 x x x 不是质数;否则是质数。

    我们可以从 质数点 x x x 出发,开始 dfs,访问非质数点,因为一条 合法路径 只存在一个质数点。

    通过上述操作,可以将 x x x 连接的几个 非质数连通块 计算出来。

    在这里插入图片描述

    假设如上图,通过 x x x 分割出了三个 非质数连通块 ,每个连通块中的节点数量分别为 2 , 3 , 4 2,3,4 2,3,4

    我们现在来计算 合法路径数量从左到右计算,保证不重不漏):

    • 1 1 1号连通块 通过 x x x 2 2 2号联通块 组成的路径数为 2 × 3 = 6 2\times 3 = 6 2×3=6
    • 1 1 1号连通块, 2 2 2号连通块 通过 x x x 3 3 3号连通块 组成的路径数为 ( 2 + 3 ) × 4 = 20 (2 + 3) \times 4 = 20 (2+3)×4=20
    • 以及所有连通块的节点数量 9 9 9

    上图通过 x x x 的合法路径条数为 6 + 20 + 9 = 35 6 + 20 + 9 = 35 6+20+9=35

    我们可以用一个数组 s z sz sz,记录下已经计算过的连通块数量。假设 1 1 1 号连通块中的两个节点分别为 i , j i , j i,j,那么 s z [ i ] = s z [ j ] = 2 sz[i] = sz[j] = 2 sz[i]=sz[j]=2

    如果下一次有另外一个 质数点 y y y 和这个 1 1 1号连通块 相连,那么我们就可以直接计算,不用再次遍历记录连通块节点的数量。

    时间复杂度: O ( n ) O(n) O(n)

    C++代码:

    using LL = long long;
    
    const int MX = 1e5;
    bool np[MX + 1];
    
    auto init = []() ->int{
        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;
            }
        }
        return 0;
    }();
    
    class Solution {
    public:
        long long countPaths(int n, vector<vector<int>>& edges) {
            vector<vector<int>> g(n + 1);
            for(auto &e:edges){
                auto a = e[0] , b = e[1];
                g[a].push_back(b);
                g[b].push_back(a);
            }
    
            vector<int> sz(n + 1);
            vector<int> nodes;
    
            function<void(int,int)> dfs = [&](int x,int fa) ->void{
                nodes.push_back(x);
                for(auto y:g[x]){
                    if(y == fa || !np[y]) continue;
                    dfs(y,x);
                }
            };
    
            LL ans = 0;
            for(int x = 1;x <= n;x++){
                if(np[x]) continue; //合数就跳过 , 是以质数为起点dfs
    
                LL sum = 0;
                for(auto y : g[x]){
                    //接着要求的是合数的连通块
                    if(!np[y]) continue;
    
                    //如果这个连通块没有被计算过,就计算
                    if(sz[y] == 0){
                        nodes.clear();
                        dfs(y,-1);
                        for(auto node:nodes) sz[node] = nodes.size();
                    }
    
                    ans += sum * sz[y];
                    sum += sz[y];
                }
    
                ans += sum;
            }
    
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    浅谈浮动flex
    继承的构造函数
    5G-Advanced核心网技术综述
    嵌入式实时操作系统的设计与开发(aCoral线程学习)
    机器学习11-聚类,孤立点判别
    使用pyvista显示有透明度信息的点云数据
    Linux中汇编语言的学习(加法、乘法、除法、左移、右移、按位与等多种命令操作实例以及ARM的 N、Z、C、V 标志位的解释)
    Cadence OrCAD Capture 如何在原理图中设置网络的飞线拓扑结构
    青少年python系列 45.文件操作1
    创建vue3项目
  • 原文地址:https://blog.csdn.net/m0_74396439/article/details/133780568