Пожалуйста, прочтите новое правило об ограничении использования AI-инструментов. ×

Блог пользователя __Serendipity

Автор __Serendipity, история, 23 месяца назад, По-английски

Given an undirected weighted graph G, with N nodes and E edges. You have to answer Q queries.

In each query, you are given two nodes u and v. The answer to the query is the minimum value k such that there is a path from node u to node v such that the maximum weight of the edge in path is at most k.

Constraints : N,E,Q <= 10^5

Can anyone please explain me how to solve this problem!

  • Проголосовать: нравится
  • -5
  • Проголосовать: не нравится

»
23 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

My guesses at solving this:

In both proposed solutions, store the max(u, v) value for each edge. We need to iterate edges in this order like Kruskal's.

Method 1: Store a DSU data structure. Store an array of arrays arr where for each node, store the query numbers involving the node. Iterate the edges in increasing order of max(u, v). If u and v have different DSU parents, merge them. Merge the smaller arr size to larger arr size. If there are common elements, the ans[common query number] = max(u, v), and exclude the common elements from the new array.

Method 2: form a minimal spanning tree using Kruskal's. Construct 2 binary lifting array of arrays, one storing the power of 2 ancestors and the other storing the max node values in the power of 2 ancestors. Finally, for each query, find the LCA and get the max(u to LCA, v to LCA).

Both solutions should run in O(nlogn). Not 100% sure about the correctness though.

  • »
    »
    23 месяца назад, # ^ |
      Проголосовать: нравится +7 Проголосовать: не нравится

    A simpler way is to add vertices in increasing order of weight and doing small to large merging with query indices stored in components. Not sure why you used edge weights.

    • »
      »
      »
      23 месяца назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Cool. Yours is a better way. I recommended dealing with edges because I thought of the problem as testing the understanding of Kruskal's MST, and wanted to incrementally add the edges. But adding nodes in increasing order will require fewer lines of code and is cleaner so that's a better approach.

      • »
        »
        »
        »
        23 месяца назад, # ^ |
          Проголосовать: нравится +8 Проголосовать: не нравится

        Ah, that makes sense. Also, is there a way to do the merging in $$$O(n \log n)$$$ without hashsets? I can only get an $$$O(n \log^2 n)$$$ solution where the small to large merging requires about $$$O(n \log n)$$$ operations, each of which cost roughly $$$O(\log n)$$$.

        • »
          »
          »
          »
          »
          23 месяца назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится

          I will use hashsets (I primarily use Python and Python sets run fairly fast, even faster than C++ unordered_set if I'm not mistaken).

          However, if you really want to avoid hashsets and maintaining O(nlogn) complexity, then you can use a linked list for each node, storing the query index in ascending order (also store the size of the linked list). At the start, populate the linked lists by iterating in increasing order of the query index. While merging, use two-pointers over the 2 linked lists (much like merge-sort) to merge/answer queries.

          • »
            »
            »
            »
            »
            »
            23 месяца назад, # ^ |
              Проголосовать: нравится +8 Проголосовать: не нравится

            The issue with merging like this is that it doesn't seem like you can merge the smaller list into the larger list in $$$O(\min(n, m))$$$ time (and I can only do this in $$$O(n + m)$$$ time).

            • »
              »
              »
              »
              »
              »
              »
              23 месяца назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится

              Oh yes you're right. Both linked lists are iterated through in the worse case. To make use of small-to-large merging's advantage, only the smaller set can be iterated through. Then I guess that a hashset would be the best bet in terms of time complexity. If the hashing algorithm has too much overheads and you're not satisfied with the treeset/ordered set's additional logn factor, then perhaps binary lifting over the MST is the next suitable option.

    • »
      »
      »
      23 месяца назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      What do you mean by "with query indices stored in components."? can you explain plzz

      • »
        »
        »
        »
        23 месяца назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Let's say for node 1 you have queries [1,2] and node 2 you have nodes [1,3,4,5]. If you union nodes 1 and 2 (due to edge 1-2 being the next edge having minimum max(a[1],a[2]), then you get a new component. Make ans[query1]=max(a[1],a[2]) and queries[par[1]]=[2,3,4,5]. Note that after union, par[1]=par[2]. The query indices stored in component [1,2] are [2,3,4,5]. Do use sets instead of arrays for small time complexity.

  • »
    »
    23 месяца назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    Thanks for sharing YMSeah

  • »
    »
    23 месяца назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    I implemented the solution in JAVA using method 2.

    Code :

    /*
            "Everything in the universe is balanced. Every disappointment
                    you face in life will be balanced by something good for you!
                            Keep going, never give up."
    
                            Just have Patience + 1...
    
    */
    
    
    
    
    
    
    
    
    
    import java.util.*;
    import java.lang.*;
    import java.io.*;
    
    
    public class Solution {
    
        static class UnionFind {
            int[] parent;
            int[] rank;
    
            UnionFind(int n) {
                parent = new int[n + 1];
                rank = new int[n + 1];
    
                for (int i = 0; i <= n; i++) {
                    parent[i] = i;
                    rank[i] = 0;
                }
            }
    
            public int find(int u) {
                if (u == parent[u]) {
                    return u;
                }
                return parent[u] = find(parent[u]);
            }
    
            public boolean unite(int u, int v) {
                int rootU = find(u);
                int rootV = find(v);
    
                if (rootU == rootV) {
                    return false;
                }
    
                if (rank[rootU] <= rank[rootV]) {
                    parent[rootU] = rootV;
                    rank[rootV] += rank[rootU];
                }else {
                    parent[rootV] = rootU;
                    rank[rootU] += rank[rootV];
                }
    
                return true;
            }
        }
    
        static class Edge implements Comparable<Edge> {
            int u;
            int v;
            int w;
    
            Edge (int u, int v, int w) {
                this.u = u;
                this.v = v;
                this.w = w;
            }
    
            public int compareTo(Edge e) {
                return Integer.compare(w, e.w);
            }
        }
    
        static class Pair {
            int to;
            int w;
    
            Pair (int to, int w) {
                this.to = to;
                this.w = w;
            }
        }
    
        static int MAX = 100005, DEPTH = 20;
        static List<Pair>[] graph = new List[MAX];
        static List<Pair>[] minSpanningTree = new List[MAX];
        static List<Edge> edges = new ArrayList<>();
        static int[][] ancestor = new int[MAX][DEPTH];
        static int[][] maxWeight = new int[MAX][DEPTH];
        static int[] depth = new int[MAX];
        static int[] tin = new int[MAX];
        static int[] tout = new int[MAX];
        static int n, time;
    
        public static void main(String[] args) throws Exception {
            out = new PrintWriter(new BufferedOutputStream(System.out));
            sc = new FastReader();
    
            int test = sc.nextInt();
            for (int t = 1; t <= test; t++) {
                solve(t);
            }
            out.close();
        }
    
        private static void solve(int t) {
            n = sc.nextInt();
            time = 0;
            int m = sc.nextInt();
            int q = sc.nextInt();
            for (int i = 0; i < n; i++) {
                graph[i] = new ArrayList<>();
                minSpanningTree[i] = new ArrayList<>();
                Arrays.fill(ancestor[i], 0);
                Arrays.fill(maxWeight[i], 0);
                tin[i] = tout[i] = depth[i] = 0;
            }
            edges.clear();
            for (int i = 0; i < m; i++) {
                int u = sc.nextInt() - 1;
                int v = sc.nextInt() - 1;
                int w = sc.nextInt();
                edges.add(new Edge(u, v, w));
                graph[u].add(new Pair(v, w));
                graph[v].add(new Pair(u, w));
            }
            kruskalMST();
            dfs(0, -1, 0);
            for (int i = 0; i < n; i++) {
                for (int j = 1; j < DEPTH; j++) {
                    ancestor[i][j] = ancestor[ancestor[i][j - 1]][j - 1];
                    maxWeight[i][j] = Math.max(maxWeight[i][j - 1], maxWeight[ancestor[i][j - 1]][j - 1]);
                }
            }
            for (int i = 0; i < q; i++) {
                int u = sc.nextInt() - 1;
                int v = sc.nextInt() - 1;
                out.println("from " + (u + 1) + " to " + (v + 1) + " -> " + maxWeightOnPath(u, v));
            }
        }
    
        private static int maxWeightOnPath(int u, int v) {
            int lcaNode = findLCA(u, v);
            int maxLeft = 0, maxRight = 0;
            for (int j = 0; j < DEPTH; j++) {
                if (isAncestor(lcaNode, ancestor[u][j])) {
                    maxLeft = Math.max(maxLeft, maxWeight[u][j]);
                }
                if (isAncestor(lcaNode, ancestor[v][j])) {
                    maxRight = Math.max(maxRight, maxWeight[v][j]);
                }
            }
            return Math.max(maxLeft, maxRight);
        }
    
        private static int findLCA(int u, int v) {
            if (depth[v] > depth[u]) {
                int temp = u;
                u = v;
                v = temp;
            }
            int diff = depth[u] - depth[v];
            u = jump(u, diff);
            if (u == v) {
                return u;
            }
            for (int i = DEPTH - 1; i >= 0; i--) {
                if (ancestor[u][i] != ancestor[v][i]) {
                    u = ancestor[u][i];
                    v = ancestor[v][i];
                }
            }
            return ancestor[u][0];
        }
    
        private static int jump(int u, int diff) {
            for (int j = DEPTH - 1; j >= 0; j--) {
                if ((diff & (1 << j)) >= 1) {
                    u = ancestor[u][j];
                }
            }
            return u;
        }
    
        private static boolean isAncestor(int u, int v) { // u is ancestor of v
            if (u == v) {
                return true;
            }
            return tin[u] < tin[v] && tout[u] > tout[v];
        }
    
        private static void dfs(int currNode, int parent, int currDepth) {
            tin[currNode] = time++;
            depth[currNode] = currDepth;
            for (Pair adjacentNode : minSpanningTree[currNode]) {
                int to = adjacentNode.to;
                int w = adjacentNode.w;
                if (to == parent) {
                    continue;
                }
                ancestor[to][0] = currNode;
                maxWeight[to][0] = w;
                dfs(to, currNode, currDepth + 1);
            }
            tout[currNode] = time++;
        }
    
        private static void kruskalMST() {
            Collections.sort(edges);
            UnionFind uf = new UnionFind(n);
            for (Edge edge : edges) {
                int u = edge.u;
                int v = edge.v;
                int w = edge.w;
                if (uf.find(u) != uf.find(v)) {
                    uf.unite(u, v);
                    minSpanningTree[u].add(new Pair(v, w));
                    minSpanningTree[v].add(new Pair(u, w));
                }
            }
        }
    
        public static FastReader sc;
        public static PrintWriter out;
        static class FastReader
        {
            BufferedReader br;
            StringTokenizer str;
    
            public FastReader()
            {
                br = new BufferedReader(new
                        InputStreamReader(System.in));
            }
    
            String next()
            {
                while (str == null || !str.hasMoreElements())
                {
                    try
    
                    {
                        str = new StringTokenizer(br.readLine());
                    }
                    catch (IOException  lastMonthOfVacation)
                    {
                        lastMonthOfVacation.printStackTrace();
                    }
    
                }
                return str.nextToken();
            }
    
            int nextInt()
            {
                return Integer.parseInt(next());
            }
    
            long nextLong()
            {
                return Long.parseLong(next());
            }
    
            double nextDouble()
            {
                return Double.parseDouble(next());
            }
    
            String nextLine()
            {
                String str = "";
                try
                {
                    str = br.readLine();
                }
                catch (IOException lastMonthOfVacation)
                {
                    lastMonthOfVacation.printStackTrace();
                }
                return str;
            }
        }
    }
    
    
    /*
    Sample Input : 
    
    1
    8
    9
    11
    1 2 15
    1 3 16
    2 3 4
    2 4 12
    2 5 3
    5 6 1
    5 7 20
    5 3 10
    3 8 8
    6 8
    6 6
    6 5
    6 2
    6 1
    5 2
    5 1
    4 3
    4 8
    4 6
    4 7
    
    Sample Output : 
    from 6 to 8 -> 8
    from 6 to 6 -> 0
    from 6 to 5 -> 1
    from 6 to 2 -> 3
    from 6 to 1 -> 15
    from 5 to 2 -> 3
    from 5 to 1 -> 15
    from 4 to 3 -> 12
    from 4 to 8 -> 12
    from 4 to 6 -> 12
    from 4 to 7 -> 20
    */
    
    
    
»
23 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

The hiring challenge is going on. Please don't post these kinds of questions before the challenge is completed. https://assessment.hackerearth.com/challenges/hiring/beaconstac-winter-internship-challenge-2022/

»
23 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится