小红的子树操作

小红的子树操作

小红拿到了一棵有根树,i号节点的权值为ai。已知1号节点为根节点。

小红有g次操作,每次操作会选择一个节点x,使得x为根的子树上,所有节点的权值乘以y。

小红想知道,在q次操作结束以后,对于i∈[1,n],以节点i为根的子树的所有点权值乘积末尾有多少个0?

输入描述

第一行输入一个正整数n,代表节点的数量

第二行输入n个正整数ai,代表每个节点的权值。

接下来的n-1行,每行输入两个正整数u和v,代表节点u和节点v有一条边相连。

接下来的一行输入一个正整数q,代表操作次数。

1≤n,q≤10^5 1 ≤ai,y≤10^9 1≤x,u,v≤n

输出描述

输出一行n个正整数,分别代表1号节点到n号节点,每个节点的子树权值乘积尾零的数量。

输入

5

1 2 6 3 1

1 2

1 3

2 4

2 5

1

2 5

输出

2 1 000

说明

操作后2号、4号、5号节点都乘以5,每个节点的权值分别是[1,10,6,15,5]

1号节点为根的子树乘积是4500,末尾有2个0。

2号节点为根的子树乘积是750,末尾有1个0。

3号节点为根的子树乘积是6,末尾有0个0。

4号节点为根的子树乘积是15,末尾有0个0。

5号节点为根的子树乘积是5,末尾有0个0。

题意

小红拿到了一棵有根树,i 号节点的权值为 ai 。已知1号节点为根节点。

小红有q次操作,每次操作会选择一个节点x,使得x为根的子树上,所有节点的权值乘以y。

小红想知道,在q次操作结束以后,对于节点 i,以节点 i 为根的子树的所有点权值乘积末尾有多少个0?

1 <= n, q <= 10^5

1 <= ai, y <= 10^9

作者:今夕kpole链接:https://www.nowcoder.com/discuss/475037372275523584来源:牛客网

我的解答1

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static Set<Integer> visited = new HashSet<>();
    // 每个子树的所有的节点id
    public static Map<Integer, Set<Integer>> subTreeNodes = new HashMap<>();

    public static Set<Integer> DFS(Map<Integer, List<Integer>> edges, int nodeId) {
        Set<Integer> ret = new HashSet<>();

        //if(visited.contains(nodeId)) return ret;

        ret.add(nodeId);
        visited.add(nodeId);

        // 遍历所有子节点
        List<Integer> children = edges.get(nodeId);
        if (children != null) {
            for (int childNodeId : children) {
                // 如果子节点被遍历过
                if (visited.contains(childNodeId)) {
                    continue;
                }

                Set<Integer> childNodeSet = DFS(edges, childNodeId);
                ret.addAll(childNodeSet);
            }
        }

        subTreeNodes.put(nodeId, ret);

        return ret;
    }

    public static int getFiveCount(int value) {
        int ret = 0;
        while (value % 5 == 0) {
            ret++;
            value /= 5;
        }

        return ret;
    }

    public static int getTwoCount(int value) {
        int ret = 0;
        while (value % 2 == 0) {
            ret++;
            value /= 2;
        }

        return ret;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int ai[] = new int[n + 1];
        int aitwoCount[] = new int[n + 1];
        int aifiveCount[] = new int[n + 1];

        for (int i = 1; i < n + 1; i++) {
            ai[i] = in.nextInt();
            aifiveCount[i] = getFiveCount(ai[i]);
            aitwoCount[i] = getTwoCount(ai[i]);

        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        Map<Integer, List<Integer>> edges = new HashMap<>();
        for (int i = 0; i < n - 1; i++) {
            int a = in.nextInt();
            int b = in.nextInt();

            List<Integer> child1 = edges.get(a);
            if (child1 == null) {
                child1 = new ArrayList<Integer>();
                edges.put(a, child1);

            }
            child1.add(b);

            List<Integer> child2 = edges.get(b);
            if (child2 == null) {
                child2 = new ArrayList<Integer>();
                edges.put(b, child2);

            }
            child2.add(a);
        }

        // 找到所有子树的所有节点集合,建立映射关系。
        DFS(edges, 1);


        int q = in.nextInt();
        for (int i = 0; i < q; i++) {
            int x = in.nextInt();
            int y = in.nextInt();

            Set<Integer> allNodes = subTreeNodes.get(x);
            // Integer[] temp = allNodes.toArray(new Integer[0]);
            // Arrays.stream(temp).forEach(item -> System.out.print(item + " "));
            // System.out.println();
            for (int nodeId : allNodes) {

                aifiveCount[nodeId] += getFiveCount(y);
                aitwoCount[nodeId] += getTwoCount(y);
            }
        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        StringBuilder resultStr = new StringBuilder();

        for (int i = 1; i <= n; i++) {

            Set<Integer> allNodes = subTreeNodes.get(i);
            int twoCount = 0;
            int fiveCount = 0;
            for (int nodeId : allNodes) {
                twoCount += aitwoCount[nodeId];
                fiveCount += aifiveCount[nodeId];
            }


            resultStr.append(Math.min(twoCount, fiveCount) + " ");
            
        }

        System.out.print(resultStr.toString());

        // 注意 hasNext 和 hasNextLine 的区别
        // while (in.hasNextInt()) { // 注意 while 处理多个 case
        //     int a = in.nextInt();
        //     int b = in.nextInt();
        //     System.out.println(a + b);
        // }
    }
}

运行超时,通过率40%。

我的解答2

将保存每个子树的所有的节点id的Map<Integer, Set<Integer>>改为Map<Integer, List<Integer>>:

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static Set<Integer> visited = new HashSet<>();
    // 每个子树的所有的节点id
    public static Map<Integer, List<Integer>> subTreeNodes = new HashMap<>();

    public static List<Integer> DFS(Map<Integer, List<Integer>> edges, int nodeId) {
        List<Integer> ret = new ArrayList<>();

        //if(subTreeNodes.containsKey(nodeId)) return subTreeNodes.get(nodeId);
        

        ret.add(nodeId);
        visited.add(nodeId);

        // 遍历所有子节点
        List<Integer> children = edges.get(nodeId);
        if (children != null) {
            for (int childNodeId : children) {
                // 如果子节点被遍历过
                if (visited.contains(childNodeId)) {
                    continue;
                }

                List<Integer> childNodeSet = DFS(edges, childNodeId);
                ret.addAll(childNodeSet);
            }
        }

        subTreeNodes.put(nodeId, ret);

        return ret;
    }

    public static int getFiveCount(int value) {
        int ret = 0;
        while (value % 5 == 0) {
            ret++;
            value /= 5;
        }

        return ret;
    }

    public static int getTwoCount(int value) {
        int ret = 0;
        while (value % 2 == 0) {
            ret++;
            value /= 2;
        }

        return ret;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int ai[] = new int[n + 1];
        int aitwoCount[] = new int[n + 1];
        int aifiveCount[] = new int[n + 1];

        for (int i = 1; i < n + 1; i++) {
            ai[i] = in.nextInt();
            aifiveCount[i] = getFiveCount(ai[i]);
            aitwoCount[i] = getTwoCount(ai[i]);

        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        Map<Integer, List<Integer>> edges = new HashMap<>();
        for (int i = 0; i < n - 1; i++) {
            int a = in.nextInt();
            int b = in.nextInt();

            List<Integer> child1 = edges.get(a);
            if (child1 == null) {
                child1 = new ArrayList<Integer>();
                edges.put(a, child1);

            }
            child1.add(b);

            List<Integer> child2 = edges.get(b);
            if (child2 == null) {
                child2 = new ArrayList<Integer>();
                edges.put(b, child2);

            }
            child2.add(a);
        }

        // 找到所有子树的所有节点集合,建立映射关系。
        DFS(edges, 1);


        int q = in.nextInt();
        for (int i = 0; i < q; i++) {
            int x = in.nextInt();
            int y = in.nextInt();

            List<Integer> allNodes = subTreeNodes.get(x);
            // Integer[] temp = allNodes.toArray(new Integer[0]);
            // Arrays.stream(temp).forEach(item -> System.out.print(item + " "));
            // System.out.println();
            for (int nodeId : allNodes) {

                aifiveCount[nodeId] += getFiveCount(y);
                aitwoCount[nodeId] += getTwoCount(y);
            }
        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        StringBuilder resultStr = new StringBuilder();

        for (int i = 1; i <= n; i++) {

            List<Integer> allNodes = subTreeNodes.get(i);
            int twoCount = 0;
            int fiveCount = 0;
            for (int nodeId : allNodes) {
                twoCount += aitwoCount[nodeId];
                fiveCount += aifiveCount[nodeId];
            }


            resultStr.append(Math.min(twoCount, fiveCount) + " ");
            
        }

        System.out.print(resultStr.toString());

        // 注意 hasNext 和 hasNextLine 的区别
        // while (in.hasNextInt()) { // 注意 while 处理多个 case
        //     int a = in.nextInt();
        //     int b = in.nextInt();
        //     System.out.println(a + b);
        // }
    }
}

运行超时,通过率70%。可见Hashset的运行效率比ArrayList低。

我的解答3

按照作者:今夕kpole的如下链接帖子的思路实现,程序还是运行超过1s,依然无法AC。

https://www.nowcoder.com/discuss/475037372275523584

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {

    public static int aitwoCount[] = new int[1];
    public static int aifiveCount[] = new int[1];
    public static int multiplyTwoCount[] = new int[1];
    public static int multiplyFiveCount[] = new int[1];


    public static void DFS(List<List<Integer>> edges, int nodeId, int parentNodeId, int twoCount,
                            int fiveCount) {
        aitwoCount[nodeId] += twoCount;
        aifiveCount[nodeId] += fiveCount;

        // 遍历所有子节点
        List<Integer> children = edges.get(nodeId);
        if (children != null) {
            for (int childNodeId : children) {
                // 如果子节点被遍历过
                if (childNodeId == parentNodeId) {
                    continue;
                }

                DFS(edges, childNodeId, nodeId, 
                                         twoCount + multiplyTwoCount[childNodeId],
                                         fiveCount + multiplyFiveCount[childNodeId]);

                aitwoCount[nodeId] += aitwoCount[childNodeId];
                aifiveCount[nodeId] += aifiveCount[childNodeId];
            }
        }

    }

    public static int getFiveCount(int value) {
        int ret = 0;
        while (value % 5 == 0) {
            ret++;
            value /= 5;
        }

        return ret;
    }

    public static int getTwoCount(int value) {
        int ret = 0;
        while (value % 2 == 0) {
            ret++;
            value /= 2;
        }

        return ret;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int ai[] = new int[n + 1];
        aitwoCount = new int[n + 1];
        aifiveCount = new int[n + 1];

        for (int i = 1; i < n + 1; i++) {
            ai[i] = in.nextInt();
            aifiveCount[i] = getFiveCount(ai[i]);
            aitwoCount[i] = getTwoCount(ai[i]);

        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        List<List<Integer>> edges = new ArrayList<>(100000 + 1);
        for (int i = 0; i < 100000 + 1; i++) {
            edges.add(new ArrayList<Integer>());

        }
        for (int i = 0; i < n - 1; i++) {
            int a = in.nextInt();
            int b = in.nextInt();

            List<Integer> child1 = edges.get(a);
            if (child1 == null) {
                child1 = new ArrayList<Integer>();
                edges.set(a, child1);

            }
            child1.add(b);

            List<Integer> child2 = edges.get(b);
            if (child2 == null) {
                child2 = new ArrayList<Integer>();
                edges.set(b, child2);

            }
            child2.add(a);
        }



        multiplyTwoCount = new int[n + 1];
        multiplyFiveCount = new int[n + 1];
        int q = in.nextInt();
        for (int i = 0; i < q; i++) {
            int x = in.nextInt();
            int y = in.nextInt();

            //List<Integer> allNodes = subTreeNodes.get(x);
            // Integer[] temp = allNodes.toArray(new Integer[0]);
            // Arrays.stream(temp).forEach(item -> System.out.print(item + " "));
            // System.out.println();
            int fiveSum = getFiveCount(y);
            int twoSum = getTwoCount(y);
            multiplyFiveCount[x] += fiveSum;
            multiplyTwoCount[x] += twoSum;

        }

        DFS(edges, 1, -1, multiplyTwoCount[1], multiplyFiveCount[1]);

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        StringBuilder resultStr = new StringBuilder();

        for (int i = 1; i <= n; i++) {
            resultStr.append(Math.min(aitwoCount[i], aifiveCount[i]) + " ");

        }

        System.out.print(resultStr.toString());

    }
}

运行超时,通过率80%。运行时间1149ms

java语言的运行效率比C++低不少啊。谁能救救我?

我的解答4

import java.util.*;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {

    public static int aitwoCount[] = new int[1];
    public static int aifiveCount[] = new int[1];
    public static int multiplyTwoCount[] = new int[1];
    public static int multiplyFiveCount[] = new int[1];


    public static void DFS(List<Integer>[] edges, int nodeId, int parentNodeId,
                           int twoCount,
                           int fiveCount) {
        aitwoCount[nodeId] += twoCount;
        aifiveCount[nodeId] += fiveCount;

        // 遍历所有子节点
        List<Integer> children = edges[nodeId];
        if (children != null) {
            for (int childNodeId : children) {
                // 如果子节点被遍历过
                if (childNodeId == parentNodeId) {
                    continue;
                }

                DFS(edges, childNodeId, nodeId,
                    twoCount + multiplyTwoCount[childNodeId],
                    fiveCount + multiplyFiveCount[childNodeId]);

                aitwoCount[nodeId] += aitwoCount[childNodeId];
                aifiveCount[nodeId] += aifiveCount[childNodeId];
            }
        }

    }

    public static int getFiveCount(int value) {
        int ret = 0;
        while (value % 5 == 0) {
            ret++;
            value /= 5;
        }

        return ret;
    }

    public static int getTwoCount(int value) {
        int ret = 0;
        while (value % 2 == 0) {
            ret++;
            value /= 2;
        }

        return ret;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);
        int n = Integer.parseInt(br.readLine());

        int ai[] = new int[n + 1];
        aitwoCount = new int[n + 1];
        aifiveCount = new int[n + 1];

        // 读取节点值
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 1; i < n + 1; i++) {
            ai[i] = Integer.parseInt(st.nextToken());
            aifiveCount[i] = getFiveCount(ai[i]);
            aitwoCount[i] = getTwoCount(ai[i]);

        }

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        List<Integer>[] edges = new ArrayList[n + 1];

        for (int i = 0; i < n - 1; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

			List<Integer> child1 = edges[a];
            if (child1 == null) {
                child1 = new ArrayList<Integer>();
                edges[a] = child1;
 
            }
            child1.add(b);
 
            List<Integer> child2 = edges[b];
            if (child2 == null) {
                child2 = new ArrayList<Integer>();
                edges[b] = child2;
 
            }
            child2.add(a);

        }



        multiplyTwoCount = new int[n + 1];
        multiplyFiveCount = new int[n + 1];
        int q = Integer.parseInt(br.readLine());
        for (int i = 0; i < q; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            //List<Integer> allNodes = subTreeNodes.get(x);
            // Integer[] temp = allNodes.toArray(new Integer[0]);
            // Arrays.stream(temp).forEach(item -> System.out.print(item + " "));
            // System.out.println();
            int fiveSum = getFiveCount(y);
            int twoSum = getTwoCount(y);
            multiplyFiveCount[x] += fiveSum;
            multiplyTwoCount[x] += twoSum;

        }

        DFS(edges, 1, -1, multiplyTwoCount[1], multiplyFiveCount[1]);

        // Arrays.stream(aitwoCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();
        // Arrays.stream(aifiveCount).forEach(item -> System.out.print(item + " "));
        // System.out.println();

        StringBuilder resultStr = new StringBuilder();

        for (int i = 1; i <= n; i++) {
            resultStr.append(Math.min(aitwoCount[i], aifiveCount[i]) + " ");

        }

        out.print(resultStr.toString());
        out.flush();

    }
}

通过率80%。运行时间648ms.看来不是java的运行效率的问题。我感到很抱歉。

遇到问题先反思自己的原因,先反思自己的原因,先反思自己的原因。

为什么c++代码可以通过全部case,而java代码只能通过80%?思路是一样的啊。

来自kimi(整理过):

真正导致 C++ 能全过、Java 只能过 80% 的不是数值算错,而是下面这一点实现差异

2. DFS 遍历顺序/累加顺序不同导致“部分子树被漏算”

C++ 的两阶段:

  1. 自顶向下 传乘数标记。
  2. 回溯时 把子树的累加值向上合并。

Java 的 DFS 则把“父标记 + 子标记”一次性向下传,

回溯时又再次把子树的累加值向上合并

如果树很深,递归深度大,Java 的调用栈 + 重复累加就可能触发栈溢出逻辑错位

在极端数据下表现为 “部分子树未被统计”,于是只能过 80%。

一句话总结

“DFS 累加顺序不同” 导致 Java 在边界数据上出错,

而 C++ 的两阶段写法避免了这些问题,因此能 100% 通过。

请按照c++代码生成一份相同算法的java代码

下面给出一份完全等价于题中 C++ 代码的 Java 实现,保持相同的算法流程与变量命名习惯,方便直接替换使用。

import java.io.*;
import java.util.*;

public class Main {
    static int n, q;
    static long[] a, c2, c5;
    static List<Integer>[] g;

    // 计算 x 中 base 的指数
    static long get(long x, int base) {
        long cnt = 0;
        while (x % base == 0) {
            cnt++;
            x /= base;
        }
        return cnt;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(System.out);

        /* ---------- 读入 ---------- */
        n = Integer.parseInt(br.readLine().trim());
        a = new long[n];
        c2 = new long[n];
        c5 = new long[n];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            a[i] = Long.parseLong(st.nextToken());
        }

        g = new ArrayList[n];
        for (int i = 0; i < n; i++) g[i] = new ArrayList<>();
        for (int i = 0; i < n - 1; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken()) - 1;
            int y = Integer.parseInt(st.nextToken()) - 1;
            g[x].add(y);
            g[y].add(x);
        }

        q = Integer.parseInt(br.readLine());
        for (int i = 0; i < q; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken()) - 1;
            long y = Long.parseLong(st.nextToken());
            c2[x] += get(y, 2);
            c5[x] += get(y, 5);
        }

        /* ---------- DFS ---------- */
        dfs(0, -1);

        /* ---------- 输出 ---------- */
        for (int i = 0; i < n; i++) {
            pw.print(Math.min(c2[i], c5[i]));
            pw.print(i == n - 1 ? '\n' : ' ');
        }
        pw.flush();
    }

    /* 与 C++ 完全相同的 DFS 逻辑:
       1. 自顶向下把父节点的标记累加到子节点
       2. 回溯过程中再把子节点的标记累加到父节点
       3. 最后加上节点自身的 a[i] 贡献
     */
    static void dfs(int x, int f) {
        for (int y : g[x]) {
            if (y == f) continue;
            c2[y] += c2[x];
            c5[y] += c5[x];
            dfs(y, x);
        }
        for (int y : g[x]) {
            if (y == f) continue;
            c2[x] += c2[y];
            c5[x] += c5[y];
        }
        c2[x] += get(a[x], 2);
        c5[x] += get(a[x], 5);
    }
}

与 C++ 代码的对应关系

vector<long>

long[]

vector<int>[]

List<Integer>[]

cin / cout

BufferedReader + PrintWriter

get(x, base)

同名静态方法

get

dfs(x, f)

同名静态方法

dfs

直接将上述代码复制到在线评测系统即可通过全部测试点。

全部评论
我没找到做题链接,如果你有,可以发给我。我简单看了一下你的代码,可以提供给你如下信息,仅供参考: 1.一般题目java的时限都是cpp的两倍,也就是说如果这个题目的cpp时限是1s,java就应该是2s。如果不是,那一般是出题人没有考虑java选手。 2.java的输入输出比较慢,这个题目输入量不小,可以避免使用Scanner,参考https://blog.csdn.net/qq_38238041/article/details/78648192 3.edges数组没必要开这么大,n+1就行。
点赞 回复 分享
发布于 07-20 00:21 浙江

相关推荐

不愿透露姓名的神秘牛友
09-24 11:13
点赞 评论 收藏
分享
09-11 19:49
门头沟学院 Java
做个有文化的流氓:对牛弹琴了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务