#include <bits/stdc++.h>
using namespace std;
const long long linf = 1e18 + 5;
const int mod = (int)1e9 + 7;
const int logN = 17;
const int inf = 1e9;
const int N = 2e5 + 5;
int hh2[N][2], s, n, m, x, y, z, a[N], root[N], h[N], hh[N][2];
vector<int> v[N];
vector<pair<int, int> > ans;
class data {
 public:
  int ans, cnt;
  data() { ans = 0, cnt = 0; }
  bool operator<(data x) {
    if (ans < x.ans) return 1;
    if (x.ans == ans && cnt < x.cnt) return 1;
    return 0;
  }
  bool operator>(data x) {
    if (x.ans < ans) return 1;
    if (x.ans == ans && x.cnt < cnt) return 1;
    return 0;
  }
  bool operator==(data x) {
    if (x.ans == ans && x.cnt == cnt) return 1;
    return 0;
  }
  data operator+(data x) {
    data t;
    t.ans = ans + x.ans;
    t.cnt = cnt + x.cnt;
    return t;
  }
  data operator-(data x) {
    data t;
    t.ans = ans - x.ans;
    t.cnt = cnt - x.cnt;
    return t;
  }
} dp[N][2], dp2[N][2];
data g(int x, int y) {
  data temp;
  temp.ans = 1;
  temp.cnt = a[x] != a[y];
  return temp;
}
data dfs(int node, int can) {
  data &r = dp2[node][can];
  if (hh2[node][can]) return r;
  hh2[node][can] = 1;
  if (!h[node]) h[node] = 1;
  data tt;
  for (__typeof(v[node].begin()) it = v[node].begin(); it != v[node].end();
       it++) {
    if (h[*it] < 2 && root[*it] == node) {
      data temp = dfs(*it, 0) + g(node, *it) - dfs(*it, 1);
      if (tt < temp) tt = temp;
      r = r + dfs(*it, 1);
    }
  }
  data r2 = r + tt;
  if (r2 > dp2[node][can] && can == 1) r = r2;
  return r;
}
void prin(int node, int can) {
  data r = dp2[node][can];
  if (!h[node]) h[node] = 1;
  data tt;
  for (__typeof(v[node].begin()) it = v[node].begin(); it != v[node].end();
       it++) {
    if (h[*it] < 2 && root[*it] == node) tt = tt + dfs(*it, 1);
  }
  if (tt == r) {
    for (__typeof(v[node].begin()) it = v[node].begin(); it != v[node].end();
         it++)
      if (h[*it] < 2 && root[*it] == node) prin(*it, 1);
    return;
  }
  bool t = 0;
  for (__typeof(v[node].begin()) it = v[node].begin(); it != v[node].end();
       it++) {
    if (h[*it] < 2 && root[*it] == node) {
      if (t) {
        prin(*it, 1);
        continue;
      }
      data temp = dfs(*it, 0) + g(node, *it) - dfs(*it, 1);
      if (r == (tt + temp)) {
        ans.push_back(make_pair(node, *it));
        prin(*it, 0);
        t = 1;
      } else
        prin(*it, 1);
    }
  }
}
data solve(int x, bool t) {
  data &r = dp[x][t];
  if (hh[x][t]) return r;
  hh[x][t] = 1;
  r.ans = 0;
  r.cnt = 0;
  if (s == x) {
    if (root[root[x]] != s) {
      r = dfs(x, 0) + dfs(root[x], 0) + solve(root[root[x]], 1) + g(x, root[x]);
    } else
      r = dfs(x, 0) + dfs(root[x], 0) + g(x, root[x]);
    data tt = dfs(x, 1) + solve(root[x], 1);
    if (tt > r) r = tt;
    tt = dfs(x, 0) + solve(root[x], 0);
    if (tt > r) r = tt;
    return r;
  }
  if (root[x] == s) {
    if (t) return r = dfs(x, 1);
    r = dfs(x, 1);
    data temp = dfs(x, 0) + g(x, root[x]);
    if (!t && temp > r) r = temp;
    return r;
  }
  if (root[root[x]] != s)
    r = dfs(x, 0) + dfs(root[x], 0) + solve(root[root[x]], t) + g(x, root[x]);
  else {
    r = dfs(x, 0) + dfs(root[x], 0) + g(x, root[x]);
  }
  data temp = dfs(x, 1) + solve(root[x], t);
  if (temp > r) r = temp;
  return r;
}
void print(int x, bool t) {
  data &r = dp[x][t];
  if (s == x) {
    if (root[root[x]] != s) {
      if (r == dfs(x, 0) + dfs(root[x], 0) + solve(root[root[x]], 1) +
                   g(x, root[x])) {
        print(root[root[x]], 1);
        prin(x, 0);
        prin(root[x], 0);
        ans.push_back(make_pair(x, root[x]));
        return;
      }
    } else {
      if (r == dfs(x, 0) + dfs(root[x], 0) + g(x, root[x])) {
        ans.push_back(make_pair(x, root[x]));
        prin(x, 0);
        prin(root[x], 0);
        return;
      }
    }
    data tt = dfs(x, 1) + solve(root[x], 1);
    if (tt == r) {
      prin(x, 1);
      print(root[x], 1);
      return;
    }
    prin(x, 0);
    print(root[x], 0);
    return;
  }
  if (root[x] == s) {
    if (t) {
      prin(x, 1);
      return;
    }
    if (r == dfs(x, 1)) {
      prin(x, 0);
      ans.push_back(make_pair(x, root[x]));
      return;
    }
    prin(x, 0);
    ans.push_back(make_pair(x, root[x]));
    return;
  }
  if (root[root[x]] != s) {
    if (r ==
        dfs(x, 0) + dfs(root[x], 0) + solve(root[root[x]], t) + g(x, root[x])) {
      prin(x, 0);
      prin(root[x], 0);
      print(root[root[x]], t);
      ans.push_back(make_pair(x, root[x]));
      return;
    }
  } else {
    if (r == dfs(x, 0) + dfs(root[x], 0) + g(x, root[x])) {
      prin(x, 0);
      prin(root[x], 0);
      ans.push_back(make_pair(x, root[x]));
      return;
    }
  }
  prin(x, 1);
  print(root[x], t);
}
data get(int x) {
  while (3 != h[x]) {
    x = root[x];
    h[x]++;
  }
  s = x;
  solve(x, 0);
  print(x, 0);
  return solve(x, 0);
}
int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d %d", &x, &a[i]);
    root[i] = x;
    v[x].push_back(i);
  }
  data ans;
  for (int i = 1; i <= n; i++) {
    if (h[i]) continue;
    ans = ans + get(i);
  }
  cout << ans.ans << ' ' << ans.cnt << '\n';
  assert((int)(::ans.size()) == ans.ans);
  int tt = 0;
  for (__typeof(::ans.begin()) it = ::ans.begin(); it != ::ans.end(); it++) {
    tt += a[it->first] != a[it->second];
    printf("%d %d\n", it->first, it->second);
  }
  assert(tt == ans.cnt);
  return 0;
}
