Hackerrank Roads in HackerLand Solution

#include <iostream>
#include <vector>
#include <bitset>
#include <queue>
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <utility>
using namespace std;
vector<int> p(100001),rnk(100001,1),child(100001);
vector<long long int> sum(400020);
vector<pair<int,pair<int,int> > > ed;
vector<vector<pair<int,int> > > mst(100001);
int n,e;
bitset<100001> vis;
int find(int a){
    return p[a]==a?a:p[a]=find(p[a]);
}
bool same(int a,int b){
    return find(a)==find(b);
}
void merge(int a,int b){
    if(!same(a,b)){
        if(rnk[find(a)]>rnk[find(b)]){
            p[find(b)]=find(a);
        }
        else {
            p[find(a)]=find(b);
        }
        if(rnk[find(a)]==rnk[find(b)])
            rnk[find(b)]++;
    }
}
int dfs(int u){
    vis[u]=1;
    int ans=1;
    for(int i=0;i<mst[u].size();i++){
        if(!vis[mst[u][i].second]){
            ans+=dfs(mst[u][i].second);
            sum[mst[u][i].first]+=(long long)(n-child[mst[u][i].second])*child[mst[u][i].second];
        }
    }
    return child[u]=ans;
}
int main(){
    ios::sync_with_stdio(0);
    cin>>n>>e;
    for(int i=1;i<=n;i++)
        p[i]=i;
    for(int i=0;i<e;i++){
        int x,y,w;
        cin>>x>>y>>w;
        ed.push_back({w,{x,y}});
    }
    sort(ed.begin(),ed.end());
    for(int i=0;i<ed.size();i++){
        if(!same(ed[i].second.first,ed[i].second.second)){
            mst[ed[i].second.first].push_back({ed[i].first,ed[i].second.second});
            mst[ed[i].second.second].push_back({ed[i].first,ed[i].second.first});
            merge(ed[i].second.first,ed[i].second.second);
        }
    }
    dfs(1);
    for(int i=0;i<2*e+10;i++){
        sum[i+1]+=sum[i]/2;
        sum[i]%=2;
    }
    int hsb=2*e+10;
    while(hsb>0&&sum[hsb]==0){
        hsb--;
    }
    for(int i=hsb;i>=0;i--)
        cout<<sum[i];
    cout<<endl;
    return 0;
}

Comments

  1. Can you explain this as well.

    ReplyDelete
    Replies
    1. Sure. This is a simple problem. We are asked to build a Minimum Spanning Tree. I did that using UFDS (Union Find Disjoint Set). It is a data structure used to organize discrete elements in multiple sets, such that an element belong to only one set. If you know this data structure, the problem becomes very easy. If you don't know it,then you can learn it or you can implement a solution using priority queue instead. This algorithm is named Kruskal's MST. The priority queue version is called Prim's MST.

      However, I would recommend that you should learn the UFDS.

      Delete
  2. Thanks i got that part of MST. What i did not get is the rest part. I tried it ,any times in python as nelow.
    #!/bin/python
    import sys
    sys.setrecursionlimit(1000000000)
    from collections import *
    import heapq
    import sys
    graph=defaultdict(dict)
    answer=0
    n,m = raw_input().strip().split(' ')
    n,m = [int(n),int(m)]
    for a1 in xrange(0,m):
    x,y,r = map(int,raw_input().strip().split(' '))
    z = 1<<r
    if y in graph[x]:
    if z < graph[x][y]:
    graph[x][y] = z
    graph[y][x] = z
    else:
    pass

    else:
    graph[x][y] = z
    graph[y][x] = z

    def dfs(node,graph1,numcities):
    if node not in visited:
    visited[node]=1
    for vertex,weight in graph1[node].items():
    countnumpaths[node][vertex] = countnumpaths.get(node,{}).get(vertex,0) + 1
    countnumpaths[vertex][node] = countnumpaths.get(vertex,{}).get(node,0) + 1
    numcities[vertex]+=1
    dfs(vertex,graph1,numcities)


    parent=[-1]*(n+1)
    visited={};heap=[];heapq.heappush(heap,(0,1))
    distance = [sys.maxint]*(n+1)
    distance[1] = 0
    while heap:
    weight,node = heapq.heappop(heap)
    if node not in visited:
    visited[node] = 0
    for node_c,weight_c in graph[node].items():
    newdist = weight+weight_c
    if newdist < distance[node_c]:
    distance[node_c] = newdist
    parent[node_c] = node
    heapq.heappush(heap,(newdist,node_c))

    graph1=defaultdict(dict)

    for i in range(2,n+1):
    graph1[parent[i]][i] = graph[parent[i]][i]
    graph1[i][parent[i]] = graph[parent[i]][i]
    visited={}
    countnumpaths=defaultdict(dict);numcities=[0]*(n+1)
    dfs(1,graph1,numcities)

    visited={}
    for i in range(1,n+1):
    for k,v in countnumpaths[i].items():
    if (i,k) not in visited:
    visited[(i,k)]=1;visited[(k,i)]=1
    answer = answer + (v*(n-v-1))*graph[i][k] if numcities[i] <= 1 or numcities[k] <=1 else answer + (v*(n-v))*graph[i][k]
    print bin(answer)[2:]

    If you cam explain the rest part after building he mst, that will help.

    ReplyDelete
  3. Hi Sir, could you please be merciful and explain to me what does the following mean:
    vector sum(400020); Thanks for your time

    ReplyDelete
    Replies
    1. Hi bro, 'sum' is just a variable name, its not a keyword. This means its not a C++ command. 'sum' is name of vector that stores the answer.

      Delete
    2. 400020 is the length of the vector. While initializing a vector we can specify size of vector, all elements then get 0 as default value.

      Delete
  4. after dsu part how have you computed dist bw every pair of cities..plz explain

    ReplyDelete
    Replies
    1. Distance between pair of cities connected by edge was given in the question. The usual solution would be to calculate shortest paths between all pais. But that approach would be too slow.

      Instead, we can take advantage of the fact that - if you added powers of 2 of distinct exponents , the result can not be greater than a power of two with any other greater exponent. For example: 2^1+2^2+2^5+2^8 < 2^9. Try to convince yourself of this.

      And I strongly suggest you take a look at 'Graphs' section at https://cp-algorithms.com/ or some other resource like 'Tushar Roy's Graph Playlist' on YouTube.

      Delete
    2. The 'sum of distinct exponents of 2' idea allows us to tackle the problem as a 'Minimum Spanning Tree' - which is more efficient than computing 'All pairs shortest path'.

      Delete
  5. Could you plz explain
    for(int i=0;i<2*e+10;i++){
    sum[i+1]+=sum[i]/2;
    sum[i]%=2;
    }
    int hsb=2*e+10;
    while(hsb>0&&sum[hsb]==0){
    hsb--;
    }
    for(int i=hsb;i>=0;i--)
    cout<<sum[i];
    ??????????????

    ReplyDelete
  6. The first 'for loop' ensures the answer is valid binary number, i.e. carry of current position is added to next higher bit. This carry works just as in decimal system we ensure no digit is more than '9'.

    The middle part of code checks for 'highest set bit' in the answer, i.e. the highest power of '2' which is part of solution.

    The last 'for loop' prints the output.

    ReplyDelete
  7. how is it guaranteed that all the pairs shortest paths are always contained in this MST?

    For Example: n=5 m=5 and a b c are

    1 2 1
    2 3 2
    3 4 4
    4 5 5
    1 5 3

    here all lengths of paths are distinct(as given in question) and Its MST will be

    1 2 1
    2 3 2
    3 4 4
    1 5 3

    so according MST distance for going from node 4 to node 5 will be 4+2+1+3=8
    BUT from actual given graph we have a path from node 4 to node 5 (which is dropped while making MST) which is of length 5 only.

    ReplyDelete
    Replies
    1. The input C_i represents a distance of 2^(C_i). So in your example, the distance is 2^4 + 2^2 + 2^1 + 2^3 = 30. Whereas the alternate path you suggest has distance 2^5 = 32; which is clearly larger. Note that 2^i is always larger than sum of (2^j)'s when j < i. Ex - 10000 (16) > 01111 (15).

      Delete
  8. Why we are doing dfs after Finding MST ?

    ReplyDelete

Post a Comment