#include <bits/stdc++.h>
using namespace std;
struct road {
  int x, next;
} r[100005 * 2];
int N, M;
int st[100005], w = 1;
int dp[100005][2], f[100005][2], with[100005][2];
int bl[100005], num[100005], tot[100005], cnt;
int s[100005], vis[100005];
int stk[100005], top, root;
int ans1, ans2, best[100005][2];
int get1, get2, tb[100005][2];
vector<int> loop[100005];
void add(int x, int y) {
  r[++w].x = y, r[w].next = st[x];
  st[x] = w;
}
bool cmp(int x, int a, int y, int b) {
  return dp[x][a] > dp[y][b] || dp[x][a] == dp[y][b] && f[x][a] > f[y][b];
}
void mx(int x, int a, int P, int S, int y) {
  if (dp[x][a] < P || dp[x][a] == P && f[x][a] < S) {
    dp[x][a] = P, f[x][a] = S;
    with[x][a] = y;
  }
}
void Find_loop(int x, int fr) {
  int i, j, tmp;
  stk[++top] = x, vis[x] = 1;
  for (i = st[x]; i; i = r[i].next) {
    tmp = r[i].x;
    if ((i ^ 1) == fr) continue;
    if (vis[tmp])
      if (!num[tmp]) {
        cnt++, loop[cnt].push_back(0);
        for (j = top; stk[j + 1] != tmp; j--)
          num[stk[j]] = ++tot[cnt], loop[cnt].push_back(stk[j]),
          bl[stk[j]] = cnt;
      } else
        ;
    else
      Find_loop(tmp, i);
  }
  top--;
}
void Work(int x, int fr) {
  int i, tmp, dpt[2], ft[2];
  vis[x] = 1;
  if (x == 5) x++, x--;
  dp[x][0] = f[x][0] = with[x][0] = with[x][1] = 0;
  dp[x][1] = f[x][1] = -0x7ffffff;
  for (i = st[x]; i; i = r[i].next) {
    tmp = r[i].x;
    if (tmp == fr) continue;
    if (bl[x])
      if (i == root || i == (root ^ 1)) continue;
    Work(tmp, x);
    dpt[0] = dp[x][0], dpt[1] = dp[x][1];
    ft[0] = f[x][0], ft[1] = f[x][1];
    mx(x, 1, dpt[1] + dp[tmp][0], ft[1] + f[tmp][0], with[x][1]);
    mx(x, 1, dpt[1] + dp[tmp][1], ft[1] + f[tmp][1], with[x][1]);
    mx(x, 1, dpt[0] + dp[tmp][0] + 1, ft[0] + f[tmp][0] + (s[x] != s[tmp]),
       tmp);
    mx(x, 0, dpt[0] + dp[tmp][0], ft[0] + f[tmp][0], 0);
    mx(x, 0, dpt[0] + dp[tmp][1], ft[0] + f[tmp][1], 0);
  }
}
void Deal(int x, int y, int fr) {
  int i, tmp, wt = with[x][y];
  if (wt) tb[++get1][0] = x, tb[get1][1] = wt, get2 += (s[x] != s[wt]);
  for (i = st[x]; i; i = r[i].next) {
    tmp = r[i].x;
    if (tmp == fr) continue;
    if (bl[x])
      if (i == root || i == (root ^ 1)) continue;
    if (tmp == wt)
      Deal(tmp, 0, x);
    else if (cmp(tmp, 0, tmp, 1))
      Deal(tmp, 0, x);
    else
      Deal(tmp, 1, x);
  }
}
void Con() {
  int i;
  for (i = 1; i <= get1; i++)
    best[++ans1][0] = tb[i][0], best[ans1][1] = tb[i][1];
  ans2 += get2;
}
int main() {
  int i, j;
  int fr, to;
  int tmp, t1, t2;
  scanf("%d", &N);
  for (i = 1; i <= N; i++) {
    scanf("%d %d", &fr, &s[i]);
    to = i, add(fr, to), add(to, fr);
  }
  for (i = 1; i <= N; i++)
    if (!vis[i]) Find_loop(i, -1);
  memset(vis, 0, sizeof(vis));
  for (i = 1; i <= cnt; i++) {
    for (j = st[loop[i][1]]; j; j = r[j].next)
      if (r[j].x == loop[i][tot[i]]) {
        root = j;
        break;
      }
    Work(loop[i][1], -1);
    get1 = get2 = 0;
    if (cmp(loop[i][1], 0, loop[i][1], 1))
      Deal(loop[i][1], 0, -1);
    else
      Deal(loop[i][1], 1, -1);
    for (j = st[loop[i][2]]; j; j = r[j].next)
      if (r[j].x == loop[i][1]) {
        root = j;
        break;
      }
    Work(loop[i][2], -1);
    if (cmp(loop[i][2], 0, loop[i][2], 1))
      t1 = dp[loop[i][2]][0], t2 = f[loop[i][2]][0];
    else
      t1 = dp[loop[i][2]][1], t2 = f[loop[i][2]][1];
    if (t1 > get1 || (t1 == get1 && t2 > get2)) {
      get1 = get2 = 0;
      if (cmp(loop[i][2], 0, loop[i][2], 1))
        Deal(loop[i][2], 0, -1);
      else
        Deal(loop[i][2], 1, -1);
    }
    Con();
  }
  for (i = 1; i <= cnt; i++)
    if (!vis[i]) {
      Work(i, -1);
      get1 = get2 = 0;
      if (cmp(i, 0, i, 1))
        Deal(i, 0, -1);
      else
        Deal(i, 1, -1);
      Con();
    }
  printf("%d %d\n", ans1, ans2);
  for (i = 1; i <= ans1; i++) printf("%d %d\n", best[i][0], best[i][1]);
  return 0;
}
