Go: package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

const (
	MAX_N      = 10000
	MAX_WEIGHT = 100000
)

type Edge struct {
	start  int
	end    int
	weight int
}

type ShortestInfo struct {
	edges    []Edge
	shortest int
}

type Neighbor struct {
	node   int
	weight int
}

func main() {
	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)

	n, err := nextInt(sc)
	if err != nil {
		panic(err)
	}

	shortestList := make([]ShortestInfo, n)
	for i := 1; i < n; i++ {
		shortestList[i] = ShortestInfo{edges: []Edge{}, shortest: MAX_N*MAX_WEIGHT + 1}
	}

	adjacentMatrix, err := createAdjacentMatrix(n, sc)
	if err != nil {
		panic(err)
	}

	sssp([]int{0}, shortestList, adjacentMatrix)
	for _, v := range shortestList {
		fmt.Printf("%d %d\n", v.getEnd(), v.shortest)
	}
}

func (si ShortestInfo) getEnd() int {
	if len(si.edges) == 0 {
		return 0
	} else {
		return si.edges[len(si.edges)-1].end
	}
}

func createAdjacentMatrix(n int, sc *bufio.Scanner) ([][]Neighbor, error) {
	adjacentMatrix := make([][]Neighbor, n)
	for i := 0; i < n; i++ {
		id, err := nextInt(sc)
		if err != nil {
			return nil, err
		}
		adjacentMatrix[id] = []Neighbor{}
		vertexNum, err := nextInt(sc)
		if err != nil {
			return nil, err
		}
		for j := 0; j < vertexNum; j++ {
			v, err := nextInt(sc)
			if err != nil {
				return nil, err
			}
			c, err := nextInt(sc)
			if err != nil {
				return nil, err
			}
			adjacentMatrix[id] = append(adjacentMatrix[id], Neighbor{v, c})
		}
	}
	return adjacentMatrix, nil
}

func sssp(nodeStack []int, shortestList []ShortestInfo, adjacentMatrix [][]Neighbor) {
	for len(nodeStack) > 0 {
		currentNode := nodeStack[0]
		nodeStack = nodeStack[1:]
		currentShortest := getShortest(currentNode, shortestList)
		adjacents := findAdjacents(currentNode, adjacentMatrix)
		for i := 0; i < len(adjacents); i++ {
			adj := adjacents[i]
			if currentShortest.shortest+adj.weight < getShortest(adj.end, shortestList).shortest {
				setShortest(adj.end, adj, currentShortest, shortestList)
				nodeStack = append(nodeStack, adj.end)
			}
		}
	}
}

func getShortest(node int, shortestList []ShortestInfo) ShortestInfo {
	return shortestList[node]
}

func setShortest(node int, edge Edge, currentShortest ShortestInfo, shortestList []ShortestInfo) {
	newEdges := make([]Edge, len(currentShortest.edges)+1)
	for i := 0; i < len(newEdges)-1; i++ {
		newEdges[i] = currentShortest.edges[i]
	}
	newEdges[len(newEdges)-1] = edge
	shortestInfo := ShortestInfo{edges: newEdges, shortest: currentShortest.shortest + edge.weight}
	shortestList[node] = shortestInfo
}

func findAdjacents(currentNode int, adjacentMatrix [][]Neighbor) []Edge {
	adjacents := adjacentMatrix[currentNode]
	edges := []Edge{}
	for _, neighbor := range adjacents {
		edges = append(edges, Edge{start: currentNode, end: neighbor.node, weight: neighbor.weight})
	}
	return edges
}

func nextInt(sc *bufio.Scanner) (int, error) {
	sc.Scan()
	return strconv.Atoi(sc.Text())
}

C++: /**
 * @brief 単一始点最短路問題をDijkstra法で解く
 */


// ********************************************************************************
// Include files
// ********************************************************************************
#include <iostream>
#include <limits>
#include <vector>
#include <queue>


// ********************************************************************************
// Type synonyms
// ********************************************************************************
using weight_t = int32_t;  // 辺(u, v)への重みwを表す型
using index_t  = int32_t;  // 頂点vの添字を表す型


// ********************************************************************************
// Constants
// ********************************************************************************
static constexpr weight_t INF = std::numeric_limits<weight_t>::max() / 3;  // 辺が存在しない場合に使用される値
static constexpr index_t  NIL = std::numeric_limits<index_t>::min()  / 3;  // 先行点が存在しない場合に使用される値

/**
 * @brief       頂点色列挙体
 * @details     (u, v) ∈ E で頂点uが黒ならば頂点vは灰か黒である。(ただしEはグラフの辺集合)
 *              すなわち、黒頂点に隣接するすべての頂点は発見済みである。
 *              灰頂点は白頂点に隣接することがあり、これらの頂点が既発見頂点と未発見頂点の境界をなす。
 */
enum vcolor : int32_t {
    WHITE,  // 未発見頂点
    BLACK,  // 既発見頂点
    GRAY,   // 既発見頂点
};


// ********************************************************************************
// Structs
// ********************************************************************************
/**< @brief グラフ用ノード(頂点) */
struct vertex {
    weight_t d;     // 始点sからの距離
    index_t pi;     // 先行頂点(の添字)
    vcolor color;   // 頂点の色
};

/**
 * @brief グラフ用エッジ(辺)
 * @note  G = (V, E)を重み関数wを持つ重み付きグラフとすると、
 *        辺(u, v) ∈ Eの重みはw(u, v)と表される。
 */
struct edge {
    index_t   src;  // 辺の始点u
    index_t   dst;  // 辺の終点v
    weight_t  w;    // 辺(u, v)の重み(コスト)
};

/**< @brief 最短路推定値をもつ頂点 (∈V-SでVはグラフの頂点集合, Sは始点sからの最短路重みが最終的に決定された頂点集合) */
struct state {
    index_t  u; // G.Vに属する頂点u
    weight_t d; // 始点sからの距離d

    // <演算子オーバーロード
    bool operator < (const state& s) const { 
        return d > s.d;     // min優先度付きキューのためのREVERSE
    }

    state(index_t u, weight_t d) : u(u), d(d) {}
};


// ********************************************************************************
// Type synonyms (part2)
// ********************************************************************************
using edges_t       = std::vector<edge>;    // グラフG=(V, E)の辺集合E
using vertices_t    = std::vector<vertex>;  // グラフG=(V, E)の頂点集合V
using graph_t       = std::vector<edges_t>; // グラフGの隣接リスト表現


// ********************************************************************************
// Functions
// ********************************************************************************

/**
 * @brief           最短路推定値dと先行点piおよび頂点色colorを初期化する
 *
 * @param[out]      vertices_t& V      グラフの頂点集合V
 * @param[in]       index_t     s      始点s
 *
 * @details         初期化の後、すべてのv∈Vについてv.pi=NIL
 *                  すべてのv∈V-{s}についてv.d=INF, v.color=WHITEである。
 */
void init(vertices_t& V, index_t s)
{
    for (auto&& v : V) {
        v.d     = INF;
        v.pi    = NIL;
        v.color = WHITE;
    }

    V[s].d     = 0;
    V[s].color = GRAY;
}

/**
 * @brief           uを経由することで、vへの既知の最短路を改善できるか判定し、
 *                  できるならば、v.dとv.piおよびv.colorを更新する。
 *                  また緩和(更新)された場合、優先度付きキューQに挿入しておく。
 *
 * @param[inout]    vertices_t&                 V   グラフの頂点集合V
 * @param[in]       const edge&                 e   辺(u, v)
 * @param[out]      std::priority_queue<state>& Q   min優先度付きキュー
 *
 * @details         緩和操作は制約v.d <= u.d + w(u, v)の緩和とみなすことができる。
 *                  最短路の性質から任意の辺(u,v)∈Eに対してδ(s,v)<=δ(s,u)+w(u,v)が成立する。
 *                  この制約はu.d=δ(s,u)かつv.d=δ(s,v)のとき必ず成立している必要がある。
 *                  v.d=u.d+w(u,v)ならばこの制約を満たさなければならないというプレッシャーが存在しないので
 *                  この制約は緩和されたことになる。(ただし、δ(s,v)は始点sから頂点vへの最短路重みを表す。)
 */
void relax(vertices_t& V, const edge& e, std::priority_queue<state>& Q)
{
    index_t u = e.src;
    index_t v = e.dst;

    // 既知の最短路を改善できるか判定する。
    if (   V[v].color != BLACK
        && V[u].d + e.w < V[v].d  ) {   // 改善できるならば、

        // 緩和を行う
        V[v].d      = V[u].d + e.w;
        V[v].pi     = u;
        V[v].color  = GRAY;

        Q.emplace(v, V[v].d);
    }
}

/**
 * @brief           単一始点最短路問題をDijkstra法で解く。
 * @param[in]       const graph_t&  G  非負の重み付き有向グラフG
 * @param[in]       index_t         s  始点s
 * @return          始点sからの最短路重みが最終的に決定された頂点の集合S
 *
 * @details         Dijkstraのアルゴリズムは、始点sからの最短路重みが最終的に決定された頂点の集合Sを管理する。
 *                  アルゴリズムは繰り返し、最小の最短路推定値を持つ頂点u∈V-Sを選択し、uをSに追加し、
 *                  uから出るすべての辺を緩和する。
 */
vertices_t dijkstra(const graph_t& G, index_t s)
{
    vertices_t S(G.size());
    std::priority_queue<state> Q;

    init(S, s);             // すべての頂点のd値とpi値を初期化する。
    Q.emplace(s, S[s].d);   // このループの最初の実行ではu=sである。
    while (!Q.empty()) {

        // Qから最小の最短路推定値を持つ頂点u(∈V-S)を取得する
        state p = Q.top();
        Q.pop();
        index_t  u = p.u;
        weight_t d = p.d;

        // 黒頂点は集合Sに属する
        S[u].color = BLACK;

        // 更新する必要がないならば飛ばす
        if (S[u].d < d) {
            continue;
        }

        // 頂点uの隣接リストに関してループをまわす
        for (const auto& e : G[u]) {    // 頂点uからでる辺(u, v)をそれぞれ緩和し、
            relax(S, e, Q);             // uを経由することでvへの最短路が改善できる場合にはv.dとv.piを更新する
        }
    }

    return S;
}


// ********************************************************************************
// Entry point
// ********************************************************************************

int main()
{
    graph_t G;
    int n, k, u, v, c;
    
    std::cin >> n;
    G.resize(n);
    for (int i = 0; i < n; i++) {
        std::cin >> u >> k;
        G[u].resize(k);
        for (int j = 0; j < k; j++) {
            std::cin >> v >> c;
            G[u][j].src = u;
            G[u][j].dst = v;
            G[u][j].w   = c;
        }
    }

    vertices_t S = dijkstra(G, 0);
    for (int i = 0; i < n; i++) {
        std::cout << i << " " << S[i].d << std::endl; 
    }
    return 0;
}