Go: package main

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

const BIG_COST = 200000

const (
	ST_NOT_YET = iota
	ST_JOINED
)

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

	nNodes := scanInt(sc)

	costs := make([][]int, nNodes)
	graph := make([][]int, nNodes)

	states := make([]int, nNodes)
	costSums := make([]int, nNodes)

	for i := 0; i < nNodes; i++ {
		costs[i] = make([]int, nNodes)
		costSums[i] = BIG_COST
	}

	for i := 0; i < nNodes; i++ {
		u := scanInt(sc)
		k := scanInt(sc)
		graph[u] = make([]int, k)
		for d := 0; d < k; d++ {
			v := scanInt(sc)
			graph[u][d] = v
			costs[u][v] = scanInt(sc)
		}
	}

	costSums[0] = 0
	for {
		minCost := BIG_COST
		var u int

		for i, cost := range costSums {
			if states[i] != ST_JOINED && cost < minCost {
				minCost = cost
				u = i
			}
		}

		if minCost == BIG_COST {
			break
		}

		states[u] = ST_JOINED

		for _, v := range graph[u] {
			cost := minCost + costs[u][v]
			if cost < costSums[v] {
				costSums[v] = cost
			}
		}
	}

	w := bufio.NewWriter(os.Stdout)
	for i, cost := range costSums {
		fmt.Fprintf(w, "%d %d\n", i, cost)
	}
	w.Flush()
}

func scanInt(sc *bufio.Scanner) int {
	sc.Scan()
	v, _ := strconv.Atoi(sc.Text())
	return v
}

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;

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

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

        // 頂点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()
{
    matrix_t M;
    int n, k, u, v, c;
    
    cin >> n; M.resize(n);
    for (int j = 0; j < n; j++) {
        M[j].resize(n);
        for (int i = 0; i < n; i++) {
            M[j][i] = INF;
        }
    }
    for (int j = 0; j < n; j++) {
        cin >> u >> k;
        for (int i = 0; i < k; i++) {
            cin >> v >> c;
            M[u][v] = c;
        }
    }
    vertices_t S = dijkstra(M, 0);
    for (int i = 0; i < n; i++) {
        cout << i << " " << S[i].d << endl; 
    }

    return 0;
}