import java.util.*; public class Main_yukicoder19 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); UnionFind uf = new UnionFind(n); List> edges = new ArrayList<>(n); for (int i = 0; i < n; i++) { edges.add(new ArrayList<>()); } int[] l = new int[n]; for (int i = 0; i < n; i++) { l[i] = sc.nextInt(); int s = sc.nextInt() - 1; uf.unite(i, s); edges.get(s).add(i); } Map hm = new HashMap<>(); for (int i = 0; i < n; i++) { double tmp = dfs(i, uf.count(i), l, edges); if (hm.containsKey(uf.find(i))) { hm.put(uf.find(i), Math.min(hm.get(uf.find(i)), tmp)); } else { hm.put(uf.find(i), tmp); } } double ret = 0; for (double e : hm.values()) { ret += e; } System.out.printf("%.1f\n", ret); sc.close(); } private static double dfs(int v, int count, int[] l, List> edges) { double ret = 0; int cnt = 0; Queue q = new ArrayDeque<>(); q.add(v); ret += l[v]; cnt++; boolean[] used = new boolean[l.length]; used[v] = true; while (!q.isEmpty()) { int e = q.remove(); for (int nv : edges.get(e)) { if (!used[nv]) { ret += (double)l[nv] / 2; cnt++; used[nv] = true; q.add(nv); } } } if (cnt == count) { return ret; } else { return Double.MAX_VALUE; } } @SuppressWarnings("unused") private static class UnionFind { int n; // cnt : 異なる集合の数 int cnt; // parent[x] : 0~n-1 の場合、要素xのroot要素 // : -1~-n の場合、自分自身がroot要素、 // -parent[x]でxを含む集合の要素数 int[] parent; UnionFind(int n) { this.n = n; cnt = n; parent = new int[n]; Arrays.fill(parent, -1); } // xのrootを求める int find(int x) { if (parent[x] < 0) { return x; } else { return parent[x] = find(parent[x]); } } // xとyが同じ集合に属するのか boolean same(int x, int y) { return find(x) == find(y); } // xとyの属する集合を併合する void unite(int x, int y) { x = find(x); y = find(y); if (x == y) { return; } cnt--; // 要素数が大きい集合をrootにする(Quick Find Weighted?) if (parent[x] > parent[y]) { parent[y] += parent[x]; parent[x] = y; } else { parent[x] += parent[y]; parent[y] = x; } return; } // 要素xを含む集合の要素数 int count(int x) { return -parent[find(x)]; } // 異なる集合の数 int count() { return cnt; } } }