#include <iostream>
#include <vector>
#include <cmath>
#include <set>

using namespace std;

int n;
vector<int> g[1000000];
set<int> gg[100001];
int f[1000000];
int s[1000000];
int visto1[1000000];
int visto2[1000000];

pair<int,int> maximo[1000000];
pair<int,int> maximosin[1000000];
int eleccion[1000000];
int eleccionsin[1000000];
vector<pair<int,int> > sol;
pair<int,int> soltotal;

void ponavisto(int u)
{
  if (visto2[u]) return;
  visto2[u]=1;
  vector<int> &ar=g[u];
  for (int i=0;i<int(ar.size());i++)
    ponavisto(ar[i]);
}

void calcula(int u,int p)
{
  vector<int> &ar=g[u];
  pair<int,int> suma(0,0);
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p) {
      calcula(v,u);
      suma.first+=maximo[v].first;
      suma.second+=maximo[v].second;
    }
  }
  maximo[u]=maximosin[u]=suma;
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p) {
      pair<int,int> nextmaximo=suma;
      nextmaximo.first+=maximosin[v].first-maximo[v].first;
      nextmaximo.second+=maximosin[v].second-maximo[v].second;
      nextmaximo.first++;
      if (s[u]!=s[v])
	nextmaximo.second++;
      if (nextmaximo>maximo[u]) {
	maximo[u]=nextmaximo;
	eleccion[u]=v;
      }
    }
  }
}

void generasol(int u,int p);

void generasolsin(int u,int p)
{
  vector<int> &ar=g[u];
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p)
      generasol(v,u);
  }
}

void generasol(int u,int p)
{
  if (eleccion[u]==0) generasolsin(u,p);
  else {
    sol.push_back(pair<int,int> (u,eleccion[u]));
    vector<int> &ar=g[u];
    for (int i=0;i<int(ar.size());i++) {
      int v=ar[i];
      if (v!=p) {
	if (v!=eleccion[u])
	  generasol(v,u);
	else
	  generasolsin(v,u);
      }
    }
  }
}

void elimina(vector<int> &ar,int u)
{
  vector<int> nextar;
  for (int i=0;i<int(ar.size());i++)
    if (ar[i]!=u)
      nextar.push_back(ar[i]);
  ar=nextar;
}

void localiza(int u,int previo,int &raiz,int &hijo1,int &hijo2)
{
  if (visto1[u]) {
    raiz=u;
    hijo1=previo;
    hijo2=f[u];
    return;
  }
  visto1[u]=1;
  localiza(f[u],u,raiz,hijo1,hijo2);
}

int main()
{
  ios::sync_with_stdio(false);
  cin>>n;
  for (int i=1;i<=n;i++) {
    cin>>f[i]>>s[i];
    if (gg[i].count(f[i])==0) {
      g[i].push_back(f[i]);
      g[f[i]].push_back(i);
      gg[i].insert(f[i]);
      gg[f[i]].insert(i);
    }
  }
  soltotal.first=soltotal.second=0;
  for (int i=1;i<=n;i++) {
    if (not visto2[i]) {
      ponavisto(i);
      int raiz,hijo1,hijo2;
      localiza(i,0,raiz,hijo1,hijo2);
      if (hijo1==hijo2) {
	calcula(raiz,0);
	generasol(raiz,0);
	soltotal.first+=maximo[raiz].first;
	soltotal.second+=maximo[raiz].second;
      } else {
	elimina(g[raiz],hijo1);
	elimina(g[hijo1],raiz);
	calcula(raiz,0);
	pair<int,int> sol1=maximo[raiz];
	g[raiz].push_back(hijo1);
	g[hijo1].push_back(raiz);
	elimina(g[raiz],hijo2);
	elimina(g[hijo2],raiz);
	calcula(raiz,0);
	pair<int,int> sol2=maximo[raiz];
	if (sol1<sol2) {
	  generasol(raiz,0);
	  soltotal.first+=maximo[raiz].first;
	  soltotal.second+=maximo[raiz].second;
	} else {
	  g[raiz].push_back(hijo2);
	  g[hijo2].push_back(raiz);
	  elimina(g[raiz],hijo1);
	  elimina(g[hijo1],raiz);
	  calcula(raiz,0);
	  generasol(raiz,0);
	  soltotal.first+=maximo[raiz].first;
	  soltotal.second+=maximo[raiz].second;
	}
      }
    }
  }
  cout<<soltotal.first<<" "<<soltotal.second<<endl;
  for (int i=0;i<int(sol.size());i++)
    cout<<sol[i].first<<" "<<sol[i].second<<endl;
}


#include <iostream>
#include <vector>
#include <cmath>
#include <set>

using namespace std;

int n;
vector<int> g[1000000];
set<int> gg[100001];
int f[1000000];
int s[1000000];
int visto1[1000000];
int visto2[1000000];

pair<int,int> maximo[1000000];
pair<int,int> maximosin[1000000];
int eleccion[1000000];
vector<pair<int,int> > sol;
pair<int,int> soltotal;

void ponavisto(int u)
{
  if (visto2[u]) return;
  visto2[u]=1;
  vector<int> &ar=g[u];
  for (int i=0;i<int(ar.size());i++)
    ponavisto(ar[i]);
}

void calcula(int u,int p)
{
  vector<int> &ar=g[u];
  pair<int,int> suma(0,0);
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p) {
      calcula(v,u);
      suma.first+=maximo[v].first;
      suma.second+=maximo[v].second;
    }
  }
  maximo[u]=maximosin[u]=suma;
  eleccion[u]=0;
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p) {
      pair<int,int> nextmaximo=suma;
      nextmaximo.first+=maximosin[v].first-maximo[v].first;
      nextmaximo.second+=maximosin[v].second-maximo[v].second;
      nextmaximo.first++;
      if (s[u]!=s[v])
	nextmaximo.second++;
      if (nextmaximo>maximo[u]) {
	maximo[u]=nextmaximo;
	eleccion[u]=v;
      }
    }
  }
}

void generasol(int u,int p);

void generasolsin(int u,int p)
{
  vector<int> &ar=g[u];
  for (int i=0;i<int(ar.size());i++) {
    int v=ar[i];
    if (v!=p)
      generasol(v,u);
  }
}

void generasol(int u,int p)
{
  if (eleccion[u]==0) generasolsin(u,p);
  else {
    sol.push_back(pair<int,int> (u,eleccion[u]));
    vector<int> &ar=g[u];
    for (int i=0;i<int(ar.size());i++) {
      int v=ar[i];
      if (v!=p) {
	if (v!=eleccion[u])
	  generasol(v,u);
	else
	  generasolsin(v,u);
      }
    }
  }
}

void elimina(vector<int> &ar,int u)
{
  vector<int> nextar;
  for (int i=0;i<int(ar.size());i++)
    if (ar[i]!=u)
      nextar.push_back(ar[i]);
  ar=nextar;
}

void localiza(int u,int previo,int &raiz,int &hijo1,int &hijo2)
{
  if (visto1[u]) {
    raiz=u;
    hijo1=previo;
    hijo2=f[u];
    return;
  }
  visto1[u]=1;
  localiza(f[u],u,raiz,hijo1,hijo2);
}

int main()
{
  ios::sync_with_stdio(false);
  cin>>n;
  for (int i=1;i<=n;i++) {
    cin>>f[i]>>s[i];
    if (gg[i].count(f[i])==0) {
      g[i].push_back(f[i]);
      g[f[i]].push_back(i);
      gg[i].insert(f[i]);
      gg[f[i]].insert(i);
    }
  }
  soltotal.first=soltotal.second=0;
  for (int i=1;i<=n;i++) {
    if (not visto2[i]) {
      ponavisto(i);
      int raiz,hijo1,hijo2;
      localiza(i,0,raiz,hijo1,hijo2);
      if (hijo1==hijo2) {
	calcula(raiz,0);
	generasol(raiz,0);
	soltotal.first+=maximo[raiz].first;
	soltotal.second+=maximo[raiz].second;
      } else {
	elimina(g[raiz],hijo1);
	elimina(g[hijo1],raiz);
	calcula(raiz,0);
	pair<int,int> sol1=maximo[raiz];
	g[raiz].push_back(hijo1);
	g[hijo1].push_back(raiz);
	elimina(g[raiz],hijo2);
	elimina(g[hijo2],raiz);
	calcula(raiz,0);
	pair<int,int> sol2=maximo[raiz];
	if (sol1<sol2) {
	  generasol(raiz,0);
	  soltotal.first+=maximo[raiz].first;
	  soltotal.second+=maximo[raiz].second;
	} else {
	  g[raiz].push_back(hijo2);
	  g[hijo2].push_back(raiz);
	  elimina(g[raiz],hijo1);
	  elimina(g[hijo1],raiz);
	  calcula(raiz,0);
	  generasol(raiz,0);
	  soltotal.first+=maximo[raiz].first;
	  soltotal.second+=maximo[raiz].second;
	}
      }
    }
  }
  cout<<soltotal.first<<" "<<soltotal.second<<endl;
  for (int i=0;i<int(sol.size());i++)
    cout<<sol[i].first<<" "<<sol[i].second<<endl;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

namespace std{
  pair<int,int> operator+(pair<int,int> a, pair<int,int> b){
    return make_pair(a.first+b.first,a.second+b.second);
  }
  pair<int,int> operator-(pair<int,int> a, pair<int,int> b){
    return make_pair(a.first-b.first,a.second-b.second);
  }
}

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF], int pre[2][BUF]){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    vector<Edge> allElect;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre);
      sum = sum+t;
    }
    ret = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre), s = rec(0,adj[cur][i],dp,adj,pre);

      if(ret<sum-t+s+make_pair(1,isMale[cur]!=isMale[adj[cur][i]])){
        ret = sum-t+s+make_pair(1,isMale[cur]!=isMale[adj[cur][i]]);
        pre[curAvail][cur] = adj[cur][i];
      }
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre);
      ret = ret+t;
    }
  }
  return ret;
}

void getEdge(int curAvail, int cur, vector<int> adj[BUF], int pre[2][BUF], vector<Edge> &toPush){
  for(int i=0;i<adj[cur].size();i++){
    if(curAvail==0 || pre[curAvail][cur]!=adj[cur][i])
      getEdge(1,adj[cur][i],adj,pre,toPush);
    else {
      toPush.push_back(Edge(cur,adj[cur][i]));
      getEdge(0,adj[cur][i],adj,pre,toPush);
    }
  }
}

void work(){
  memset(finished,0,sizeof(finished));
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  static int pre[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  memset(pre,-1,sizeof(pre));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    pair<int,int> maxV = make_pair(-1,-1);
    vector<Edge> toPush;
    for(int j=0;j<2;j++){
      int src = nex[toCalc], dst = toCalc;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      pair<int,int> A = rec(1,dst,dp[j],adj,pre[j]);

      if(maxV<A){
        maxV = A;
        toPush.clear();
        getEdge(1,dst,adj,pre[j],toPush);
      }
      adj[src].push_back(dst);

      toCalc = nex[toCalc];
    }

    num = num+maxV;
    ans.insert(ans.end(),toPush.begin(),toPush.end());
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF], int pre[2][BUF]){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    vector<Edge> allElect;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre), s = rec(0,adj[cur][i],dp,adj,pre);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;

      if(ret<make_pair(fst,scd)){
        ret = make_pair(fst,scd);
        pre[curAvail][cur] = adj[cur][i];
      }
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getEdge(int curAvail, int cur, vector<int> adj[BUF], int pre[2][BUF], vector<Edge> &toPush){
  for(int i=0;i<adj[cur].size();i++){
    if(curAvail==0 || pre[curAvail][cur]!=adj[cur][i])
      getEdge(1,adj[cur][i],adj,pre,toPush);
    else {
      toPush.push_back(Edge(cur,adj[cur][i]));
      getEdge(0,adj[cur][i],adj,pre,toPush);
    }
  }
}

void work(){
  memset(finished,0,sizeof(finished));
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  static int pre[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  memset(pre,-1,sizeof(pre));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    pair<int,int> maxV = make_pair(-1,-1);
    vector<Edge> toPush;
    for(int j=0;j<2;j++){
      int src = nex[toCalc], dst = toCalc;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      pair<int,int> A = rec(1,dst,dp[j],adj,pre[j]);

      if(maxV<A){
        maxV = A;
        toPush.clear();
        //cout << "Y" << endl;
        getEdge(1,dst,adj,pre[j],toPush);
      }
      adj[src].push_back(dst);

      toCalc = nex[toCalc];
    }

    num.first += maxV.first;
    num.second += maxV.second;
    ans.insert(ans.end(),toPush.begin(),toPush.end());
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  bool finished[BUF];
  memset(finished,0,sizeof(finished));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;
    bool visited[BUF];
    memset(visited,0,sizeof(visited));
    int cur = i;
    while(1){
      finished[cur] = visited[cur] = true;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    int src2 = cycleList[i].back().dst, dst2 = cycleList[i].back().src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));

    pair<int,int> A = rec(1,dst,dp,adj);
    adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));

    memset(dp,-1,sizeof(dp));
    pair<int,int> B = rec(0,dst,dp,adj);
    adj[src2].push_back(dst2);
    B.first++;
    B.second += isMale[src]!=isMale[dst];
    //cout << A.first << ' ' << A.second << ',' << B.first << ' ' << B.second << endl;
    if(A>B){
      num.first += A.first;
      num.second += A.second;
      getAns(1,dst,dp,ans,adj);
    }
    else{
      adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));
      num.first += B.first;
      num.second += B.second;
      getAns(0,dst,dp,ans,adj);
      ans.push_back(Edge(src,dst));
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  int visited[BUF];
  bool finished[BUF];
  memset(finished,0,sizeof(finished));
  memset(visited,-1,sizeof(visited));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int cur = i;
    while(1){
      finished[cur] = true;
      visited[cur] = i;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]==i){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  for(int i=0;i<cycleList.size();i++){
    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  bool finished[BUF];
  memset(finished,0,sizeof(finished));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;
    bool visited[BUF];
    memset(visited,0,sizeof(visited));
    int cur = i;
    while(1){
      finished[cur] = visited[cur] = true;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  bool finished[BUF];
  memset(finished,0,sizeof(finished));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;
    bool visited[BUF];
    memset(visited,0,sizeof(visited));
    int cur = i;
    while(1){
      finished[cur] = visited[cur] = true;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    int src2 = cycleList[i].back().dst, dst2 = cycleList[i].back().src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));

    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));

    pair<int,int> B = rec(0,dst,dp[1],adj);
    adj[src2].push_back(dst2);
    B.first++;
    B.second += isMale[src]!=isMale[dst];
    //cout << A.first << ' ' << A.second << ',' << B.first << ' ' << B.second << endl;
    if(A>B){
      num.first += A.first;
      num.second += A.second;
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));
      num.first += B.first;
      num.second += B.second;
      getAns(0,dst,dp[1],ans,adj);
      ans.push_back(Edge(src,dst));
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  int visited[BUF];
  bool finished[BUF];
  memset(finished,0,sizeof(finished));
  memset(visited,-1,sizeof(visited));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int cur = i;
    while(1){
      finished[cur] = true;
      visited[cur] = i;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]==i){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  bool finished[BUF];
  memset(finished,0,sizeof(finished));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;
    bool visited[BUF];
    memset(visited,0,sizeof(visited));
    int cur = i;
    while(1){
      finished[cur] = visited[cur] = true;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    int src2 = cycleList[i].back().dst, dst2 = cycleList[i].back().src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));

    pair<int,int> A = rec(1,dst,dp,adj);
    adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));
    pair<int,int> B = rec(0,dst,dp,adj);
    adj[src2].push_back(dst2);
    B.first++;
    B.second += isMale[src]!=isMale[dst];
    //cout << A.first << ' ' << A.second << ',' << B.first << ' ' << B.second << endl;
    if(A>B){
      num.first += A.first;
      num.second += A.second;
      getAns(1,dst,dp,ans,adj);
    }
    else{
      adj[src2].erase(find(adj[src2].begin(),adj[src2].end(),dst2));
      num.first += B.first;
      num.second += B.second;
      getAns(0,dst,dp,ans,adj);
      ans.push_back(Edge(src,dst));
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  //bool finished[BUF]={};
  memset(finished,0,sizeof(finished));
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    int src = nex[toCalc], dst = toCalc;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = nex[nex[toCalc]], dst = nex[toCalc];
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = nex[toCalc], dst = toCalc;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = nex[nex[toCalc]], dst = nex[toCalc];
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    cin >> nex[i] >> maleId;
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  bool finished[BUF];
  memset(finished,0,sizeof(finished));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;
    bool visited[BUF];
    memset(visited,0,sizeof(visited));
    int cur = i;
    while(1){
      finished[cur] = visited[cur] = true;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    cout << ans[i].src+1 << ' ' << ans[i].dst+1 << endl;
  cout << endl;
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF], vector<Edge> &toPush){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,toPush);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;

    int maxEdge = -1;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,toPush), s = rec(0,adj[cur][i],dp,adj,toPush);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;

      if(ret<make_pair(fst,scd)){
        ret = make_pair(fst,scd);
        maxEdge = adj[cur][i];
      }
    }
    if(maxEdge!=-1) toPush.push_back(Edge(cur,maxEdge));
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,toPush);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void work(){
  memset(finished,0,sizeof(finished));
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    vector<Edge> toPush[2];

    int src = nex[toCalc], dst = toCalc;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj,toPush[0]);
    adj[src].push_back(dst);

    src = nex[nex[toCalc]], dst = nex[toCalc];
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj,toPush[1]);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      ans.insert(ans.end(),toPush[0].begin(),toPush[0].end());
    }
    else{
      num.first += B.first;
      num.second += B.second;
      ans.insert(ans.end(),toPush[1].begin(),toPush[1].end());
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

namespace std{
  pair<int,int> operator+(pair<int,int> a, pair<int,int> b){
    return make_pair(a.first+b.first,a.second+b.second);
  }
  pair<int,int> operator-(pair<int,int> a, pair<int,int> b){
    return make_pair(a.first-b.first,a.second-b.second);
  }
}

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF], int pre[2][BUF]){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);

  pair<int,int> sum = make_pair(0,0);
  vector<Edge> allElect;
  for(int i=0;i<adj[cur].size();i++){
    pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre);
    sum = sum+t;
  }
  ret = sum;

  if(curAvail){
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj,pre), s = rec(0,adj[cur][i],dp,adj,pre);
      if(ret<sum-t+s+make_pair(1,isMale[cur]!=isMale[adj[cur][i]])){
        ret = sum-t+s+make_pair(1,isMale[cur]!=isMale[adj[cur][i]]);
        pre[curAvail][cur] = adj[cur][i];
      }
    }
  }

  return ret;
}

void getEdge(int curAvail, int cur, vector<int> adj[BUF], int pre[2][BUF], vector<Edge> &toPush){
  for(int i=0;i<adj[cur].size();i++){
    if(pre[curAvail][cur]!=adj[cur][i])
      getEdge(1,adj[cur][i],adj,pre,toPush);
    else {
      toPush.push_back(Edge(cur,adj[cur][i]));
      getEdge(0,adj[cur][i],adj,pre,toPush);
    }
  }
}

void work(){
  memset(finished,0,sizeof(finished));
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  static int pre[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  memset(pre,-1,sizeof(pre));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    pair<int,int> maxV = make_pair(-1,-1);
    vector<Edge> toPush;
    for(int j=0;j<2;j++){
      int src = nex[toCalc], dst = toCalc;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      pair<int,int> A = rec(1,dst,dp[j],adj,pre[j]);

      if(maxV<A){
        maxV = A;
        toPush.clear();
        getEdge(1,dst,adj,pre[j],toPush);
      }
      adj[src].push_back(dst);

      toCalc = nex[toCalc];
    }

    num = num+maxV;
    ans.insert(ans.end(),toPush.begin(),toPush.end());
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      rec(0,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]], &s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]];
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  int visited[BUF];
  bool finished[BUF];
  memset(finished,0,sizeof(finished));
  memset(visited,-1,sizeof(visited));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int cur = i;
    while(1){
      finished[cur] = true;
      visited[cur] = i;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]==i){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;
  for(int i=0;i<cycleList.size();i++){
    static pair<int,int> dp[2][2][BUF];
    memset(dp,-1,sizeof(dp));

    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

bool finished[BUF];

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  finished[cur] = true;
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj), s = rec(0,adj[cur][i],dp,adj);
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = rec(1,adj[cur][i],dp,adj);
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  bool finished[BUF]={};
  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int toCalc = i;
    while(!finished[toCalc]){
      finished[toCalc] = true;
      toCalc = nex[toCalc];
    }

    int src = nex[toCalc], dst = toCalc;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = nex[nex[toCalc]], dst = nex[toCalc];
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = nex[toCalc], dst = toCalc;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = nex[nex[toCalc]], dst = nex[toCalc];
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int BUF = 100005;

class Edge{
public:
  int src, dst;
  Edge(){}
  Edge(int s, int d): src(s), dst(d){}
};

int nNode, nex[BUF];
bool isMale[BUF];

bool read(){
  if(!(cin >> nNode)) return false;
  for(int i=0;i<nNode;i++){
    int maleId;
    scanf("%d%d",&nex[i],&maleId);
    nex[i]--;
    isMale[i] = maleId==1;
  }
  return true;
}

pair<int,int> rec(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<int> adj[BUF]){
  pair<int,int> &ret = dp[curAvail][cur];
  if(ret!=make_pair(-1,-1)) return ret;
  ret = make_pair(0,0);
  if(curAvail){
    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    ret = sum;
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      rec(0,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]], &s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      ret = max(ret,make_pair(fst,scd));
    }
  }
  else{
    for(int i=0;i<adj[cur].size();i++){
      rec(1,adj[cur][i],dp,adj);
      pair<int,int> &t = dp[1][adj[cur][i]];
      ret.first += t.first;
      ret.second += t.second;
    }
  }
  return ret;
}

void getAns(int curAvail, int cur, pair<int,int> dp[2][BUF], vector<Edge> &ans, vector<int> adj[BUF]){
  if(curAvail){
    int connect = -1;
    pair<int,int> maxV;

    pair<int,int> sum = make_pair(0,0);
    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]];
      sum.first += t.first;
      sum.second += t.second;
    }
    maxV = sum;

    for(int i=0;i<adj[cur].size();i++){
      pair<int,int> t = dp[1][adj[cur][i]], s = dp[0][adj[cur][i]];
      int boygirl = isMale[cur]!=isMale[adj[cur][i]];
      int fst = sum.first-t.first+s.first+1;
      int scd = sum.second-t.second+s.second+boygirl;
      if(maxV<make_pair(fst,scd)){
        maxV = make_pair(fst,scd);
        connect = adj[cur][i];
      }
    }

    if(connect!=-1)
      ans.push_back(Edge(cur,connect));
    for(int i=0;i<adj[cur].size();i++)
      getAns(adj[cur][i]==connect?0:1,adj[cur][i],dp,ans,adj);
  }
  else{
    for(int i=0;i<adj[cur].size();i++)
      getAns(1,adj[cur][i],dp,ans,adj);
  }
}

void work(){
  vector< vector<Edge> > cycleList;
  int visited[BUF];
  bool finished[BUF];
  memset(finished,0,sizeof(finished));
  memset(visited,-1,sizeof(visited));

  for(int i=0;i<nNode;i++){
    if(finished[i]) continue;

    int cur = i;
    while(1){
      finished[cur] = true;
      visited[cur] = i;
      if(finished[nex[cur]]){
        if(visited[nex[cur]]==i){
          cycleList.push_back(vector<Edge>());
          cycleList.back().push_back(Edge(cur,nex[cur]));
          int t = nex[cur];
          while(t!=cur){
            cycleList.back().push_back(Edge(t,nex[t]));
            t = nex[t];
          }
        }
        break;
      }
      cur = nex[cur];
    }
  }
  /*
  cout << "cycleList" << endl;
  for(int i=0;i<cycleList.size();i++){
    for(int j=0;j<cycleList[i].size();j++){
      cout << cycleList[i][j].src << ' ' << cycleList[i][j].dst << endl;
    }
    cout << endl;
  }cout << endl;
  */

  vector<int> adj[BUF];
  for(int i=0;i<nNode;i++)
    adj[nex[i]].push_back(i);

  pair<int,int> num = make_pair(0,0);
  vector<Edge> ans;

  static pair<int,int> dp[2][2][BUF];
  memset(dp,-1,sizeof(dp));
  for(int i=0;i<cycleList.size();i++){
    int src = cycleList[i][0].dst, dst = cycleList[i][0].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> A = rec(1,dst,dp[0],adj);
    adj[src].push_back(dst);

    src = cycleList[i][1].dst, dst = cycleList[i][1].src;
    adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
    pair<int,int> B = rec(1,dst,dp[1],adj);
    adj[src].push_back(dst);

    if(A>B){
      num.first += A.first;
      num.second += A.second;
      src = cycleList[i][0].dst, dst = cycleList[i][0].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[0],ans,adj);
    }
    else{
      num.first += B.first;
      num.second += B.second;
      src = cycleList[i][1].dst, dst = cycleList[i][1].src;
      adj[src].erase(find(adj[src].begin(),adj[src].end(),dst));
      getAns(1,dst,dp[1],ans,adj);
    }
  }

  cout << num.first << ' ' << num.second << endl;
  for(int i=0;i<ans.size();i++)
    printf("%d %d\n",ans[i].src+1,ans[i].dst+1);
}

int main(){
  while(read()) work();
  return 0;
}


#include<bits/stdc++.h>
#define st first
#define nd second
#define pb push_back
#define sz(x) ((int)x.size())
#define ii pair<int,int>
#define N 100005
#define inf 2000000000
using namespace std;

int n,frb,flag;
int s[N],u[N],go[N],bgo[N];
int c,cnt,ccnt;
ii dp[N][2],res[N][2];
ii ans;
int ok[N][2],vis[N],way[N][2];
vector<int> v[N];
vector<ii> p;

ii best(ii a,ii b) {

	if(a.st>b.st) return a;
	if(a.st<b.st) return b;

	return (a.nd>b.nd?a:b);

}

ii add(ii a,ii b) {

	return {a.st+b.st,a.nd+b.nd};

}

ii dec(ii a,ii b) {

	b={-b.st,-b.nd};

	return add(a,b);

}

void ret(int node,int w) {

	for(int i:v[node]) {

		if(vis[i]>0) continue ;

		if(w==0 && bgo[node]==i) {

			p.pb({i,node});

			ret(i,1);

		}
		else {

			if(best(dp[i][0],dp[i][1])==dp[i][0]) ret(i,0);
			else ret(i,1);

		}

	}

}

void rec(int node,int c) {

	if(way[node][c]) {

		p.pb({node,go[node]});

		ret(node,1);
		ret(go[node],1);

		if(go[go[node]]!=frb) {

			rec(go[go[node]],c);

		}

	}
	else {

		if(best(dp[node][0],dp[node][1])==dp[node][0]) {

			ret(node,0);

		}
		else {

			ret(node,1);

		}

		if(go[node]!=frb) rec(go[node],c);

	}

}

ii f(int node) {

	if(ok[node][c]) return res[node][c];

	ok[node][c]=1;

	ii r1;
	ii r2={0,0};

	if(go[node]==frb) r1=best(dp[node][0],dp[node][1]);
	else r1=add(f(go[node]),best(dp[node][0],dp[node][1]));

	if(go[node]!=frb) {

		if(go[go[node]]==frb) {

			r2=add(add(dp[node][1],dp[go[node]][1]),{1,s[node]!=s[go[node]]});

		}
		else {

			r2=add(f(go[go[node]]),add(add(dp[node][1],dp[go[node]][1]),{1,s[node]!=s[go[node]]}));

		}

	}

	ii r=best(r1,r2);

	if(r==r1) {

		way[node][c]=0;

	}
	else {

		way[node][c]=1;

	}

	return res[node][c]=r;

}

ii solve(int beg) {

	frb=beg;

	ii sol=f(beg);

	++c;

	return sol;

}

void dfs2(int node) {

	ii bc={0,0};
	int t=-1;

	for(int i:v[node]) {

		if(vis[i]>0) continue ;

		dfs2(i);

		ii w=best(dp[i][0],dp[i][1]);

		dp[node][0]=add(dp[node][0],w);
		dp[node][1]=add(dp[node][1],w);

		ii m={dp[i][1].st+1,dp[i][1].nd+(s[node]!=s[i])};

		ii f=dec(m,w);

		if(best(bc,f)==f) {

			bc=f;
			t=i;

		}

	}

	dp[node][0]=add(dp[node][0],bc);
	bgo[node]=t;

}

void dfs(int node) {

	if(vis[node]) {

		if(vis[node]==cnt) {

			flag=node;
			vis[node]=++ccnt;

		}

		return ;

	}

	vis[node]=cnt;

	for(int i:v[node]) {

		dfs(i);

		if(flag) vis[node]=ccnt;

		if(flag==node) flag=0;

	}

}

int main() {

	scanf("%d",&n);

	for(int i=1;i<=n;i++) {

		int a,b;

		scanf("%d %d",&a,&b);

		go[i]=a;
		s[i]=b;

		v[a].pb(i);

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]) continue ;

		flag=0;
		cnt--;

		dfs(i);

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]>0) {

			dfs2(i);

		}

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]>0 && !u[vis[i]]) {

			u[vis[i]]=1;

			ii a=solve(i);
			ii b=solve(go[i]);

			ii bs=best(a,b);

			ans=add(ans,bs);

			if(bs==a) {

				frb=i;
				rec(i,0);

			}
			else {

				frb=go[i];
				rec(go[i],1);

			}

			c=0;

		}

	}

	printf("%d %d\n",ans.st,ans.nd);

	for(auto x:p) printf("%d %d\n",x.st,x.nd);

}


#include<bits/stdc++.h>
#define st first
#define nd second
#define pb push_back
#define sz(x) ((int)x.size())
#define ii pair<int,int>
#define N 100005
#define inf 2000000000
using namespace std;

int n,frb,flag;
int s[N],u[N],go[N],bgo[N];
int c,cnt,ccnt;
ii dp[N][2],res[N][2];
ii ans;
int ok[N][2],vis[N],way[N][2];
vector<int> v[N];
vector<ii> p;

ii best(ii a,ii b) {

	if(a.st>b.st) return a;
	if(a.st<b.st) return b;

	return (a.nd>b.nd?a:b);

}

ii add(ii a,ii b) {

	return {a.st+b.st,a.nd+b.nd};

}

ii dec(ii a,ii b) {

	b={-b.st,-b.nd};

	return add(a,b);

}

void ret(int node,int w) {

	for(int i:v[node]) {

		if(vis[i]>0) continue ;

		if(w==0 && bgo[node]==i) {

			p.pb({i,node});

			ret(i,1);

		}
		else {

			if(best(dp[i][0],dp[i][1])==dp[i][0]) ret(i,0);
			else ret(i,1);

		}

	}

}

void rec(int node,int c) {

	if(way[node][c]) {

		p.pb({node,go[node]});

		ret(node,1);
		ret(go[node],1);

		if(go[go[node]]!=frb) {

			rec(go[go[node]],c);

		}

	}
	else {

		if(best(dp[node][0],dp[node][1])==dp[node][0]) {

			ret(node,0);

		}
		else {

			ret(node,1);

		}

		if(go[node]!=frb) rec(go[node],c);

	}

}

ii f(int node) {

	if(ok[node][c]) return res[node][c];

	ok[node][c]=1;

	ii r1;
	ii r2={0,0};

	if(go[node]==frb) r1=best(dp[node][0],dp[node][1]);
	else r1=add(f(go[node]),best(dp[node][0],dp[node][1]));

	if(go[node]!=frb) {

		if(go[go[node]]==frb) {

			r2=add(add(dp[node][1],dp[go[node]][1]),{1,s[node]!=s[go[node]]});

		}
		else {

			r2=add(f(go[go[node]]),add(add(dp[node][1],dp[go[node]][1]),{1,s[node]!=s[go[node]]}));

		}

	}

	ii r=best(r1,r2);

	if(r==r1) {

		way[node][c]=0;

	}
	else {

		way[node][c]=1;

	}

	return res[node][c]=r;

}

ii solve(int beg) {

	frb=beg;

	ii sol=f(beg);

	++c;

	return sol;

}

void dfs2(int node) {

	ii bc={0,0};
	int t=-1;

	for(int i:v[node]) {

		if(vis[i]>0) continue ;

		dfs2(i);

		ii w=best(dp[i][0],dp[i][1]);

		dp[node][0]=add(dp[node][0],w);
		dp[node][1]=add(dp[node][1],w);

		ii m={dp[i][1].st+1,dp[i][1].nd+(s[node]!=s[i])};

		ii f=dec(m,w);

		if(best(bc,f)==f) {

			bc=f;
			t=i;

		}

	}

	dp[node][0]=add(dp[node][0],bc);
	bgo[node]=t;

}

void dfs(int node) {

	if(vis[node]) {

		if(vis[node]==cnt) {

			flag=node;
			vis[node]=++ccnt;

		}

		return ;

	}

	vis[node]=cnt;

	dfs(go[node]);

	if(flag) vis[node]=ccnt;

	if(flag==node) flag=0;

}

int main() {

	scanf("%d",&n);

	for(int i=1;i<=n;i++) {

		int a,b;

		scanf("%d %d",&a,&b);

		go[i]=a;
		s[i]=b;

		v[a].pb(i);

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]) continue ;

		flag=0;
		cnt--;

		dfs(i);

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]>0) {

			dfs2(i);

		}

	}

	for(int i=1;i<=n;i++) {

		if(vis[i]>0 && !u[vis[i]]) {

			u[vis[i]]=1;

			ii a=solve(i);
			ii b=solve(go[i]);

			ii bs=best(a,b);

			ans=add(ans,bs);

			if(bs==a) {

				frb=i;
				rec(i,0);

			}
			else {

				frb=go[i];
				rec(go[i],1);

			}

			c=0;

		}

	}

	printf("%d %d\n",ans.st,ans.nd);

	for(auto x:p) printf("%d %d\n",x.st,x.nd);

}


#include <bits/stdc++.h>
using namespace std;
#define PB push_back
#define ZERO (1e-10)
#define INF int(1e9+1)
#define CL(A,I) (memset(A,I,sizeof(A)))
#define DEB printf("DEB!\n");
#define D(X) cout<<"  "<<#X": "<<X<<endl;
#define EQ(A,B) (A+ZERO>B&&A-ZERO<B)
typedef long long ll;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef pair<int,int> ii;
typedef vector<ii> vii;
#define IN(n) int n;scanf("%d",&n);
#define FOR(i, m, n) for (int i(m); i < n; i++)
#define F(n) FOR(i,0,n)
#define FF(n) FOR(j,0,n)
#define FT(m, n) FOR(k, m, n)
#define aa first
#define bb second
void ga(int N,int *A){F(N)scanf("%d",A+i);}
#define MX 100005
vi g[MX];
int G[MX],X[MX],a,N,b,D[MX],dd[MX][2][2],C[MX],S;
ii dp[MX][2][2];
vii o;
void dl(int a,int b){F((int)g[a].size())if(g[a][i]==b)g[a][i]=g[a].back(),g[a].pop_back();}
ii dyn(int u,int p,bool x,bool y){
    ii&v=dp[u][x][y],a,b,c,S;
    int&w=dd[u][x][y];
    if(~w)return v;
    for(auto&h:g[u])if(h^p)a=dyn(h,u,0,y),S.aa+=a.aa,S.bb+=a.bb;
    v=S,w=-INF;
    if(!x)for(auto&h:g[u])if(h^p){
        a=dyn(h,u,0,y),b=dyn(h,u,1,y),c={S.aa+b.aa-a.aa+1,S.bb+b.bb-a.bb+(X[u]!=X[h])};
        if(c>v)v=c,w=h;
    }
//    printf("[%d](%d){%d}: [%d,%d]\n",u+1,p+1,x,v.aa,v.bb);
    return v;
}
void pt(int u,int p,bool x,bool y){
    int w=dd[u][x][y];
    if(w==-INF){
        for(auto&h:g[u])if(h^p)pt(h,u,0,y);
        return;
    }
    o.PB({u,w});
    for(auto&h:g[u])if(h^p)pt(h,u,h==w,y);
}
void dfs(int u,int p){
    C[u]++;
    if(!C[G[u]])return dfs(G[u],u);
    ii a,b;
    if(G[u]==p)return a=dyn(u,u,0,0),S+=a.bb,pt(u,u,0,0);
    dl(u,p),dl(p,u),a=dyn(u,u,0,0),g[u].PB(p),g[p].PB(u);
    dl(u,G[u]),dl(G[u],u),b=dyn(u,u,0,1);
    if(a<b)pt(u,u,0,1),S+=b.bb,g[u].PB(G[u]),g[G[u]].PB(u);
    else dl(u,p),dl(p,u),g[u].PB(G[u]),g[G[u]].PB(u),pt(u,u,0,0),S+=a.bb,g[p].PB(u),g[u].PB(p);
}
void clr(int u){if(D[u]++)return;for(auto&h:g[u])clr(h);}
int main(void){
    scanf("%d",&N),CL(dd,-1);
    F(N)scanf("%d%d",&a,X+i),G[i]=--a,g[i].PB(a),g[a].PB(i);
    F(N)sort(g[i].begin(),g[i].end()),g[i].resize(unique(g[i].begin(),g[i].end())-g[i].begin());
    F(N)if(!D[i])dfs(i,-1),clr(i);
    printf("%lu %d\n",o.size(),S);
    for(auto&h:o)printf("%d %d\n",h.aa+1,h.bb+1);
    return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD1 1000000007
#define lson(v) ((v)<<1)
#define rson(v) (((v)<<1)^1)
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
int n,f[100005],s[100005],vis[100005],vis2[100005],pf[2][100005],ext[100005],cu,cv,cc;
pii dp[100005][2],dp2[100005][2];
vector < int > cur;
vector < pii > adj[100005],son[100005],as1,as2;
vector < vector < pii > > fstp;
pii operator +(pii a,pii b){return mpr(a.first+b.first,a.second+b.second);}
pii operator -(pii a,pii b){return mpr(a.first-b.first,a.second-b.second);}
void dfs(int ver,int prev){
	//printf("dfs(%d,%d)\n",ver+1,prev+1);
	vis[ver]=1;
	cur.push_back(ver);
	int xb;
	pii addon=mpr(-iinf,-iinf);
	bool vs=0;
	for(xb=0;xb<adj[ver].size();++xb){
		if(adj[ver][xb].first!=prev){
			if(!vis[adj[ver][xb].first]){
				vs=1;
				son[ver].push_back(adj[ver][xb]);
				dfs(adj[ver][xb].first,ver);
				dp[ver][0]=dp[ver][0]+MAX(dp[adj[ver][xb].first][1],dp[adj[ver][xb].first][0]);
				if(mpr(1,adj[ver][xb].second)+dp[adj[ver][xb].first][0]-MAX(dp[adj[ver][xb].first][1],dp[adj[ver][xb].first][0])>addon){
					addon=mpr(1,adj[ver][xb].second)+dp[adj[ver][xb].first][0]-MAX(dp[adj[ver][xb].first][1],dp[adj[ver][xb].first][0]);
					pf[0][ver]=adj[ver][xb].first;
				}
			}
			else if(cu==-1){
				cu=ver;
				cv=adj[ver][xb].first;
				cc=adj[ver][xb].second;
			}
		}
	}
	if(vs==0){
		dp[ver][0]=dp[ver][1]=mpr(0,0);
	}
	else{
		dp[ver][1]=dp[ver][0]+addon;
	}
	//printf("dp[%d]:{%d,%d ; %d,%d}\n",ver+1,dp[ver][0].first,dp[ver][0].second,dp[ver][1].first,dp[ver][1].second);
}
void pick(int ver,int type){
	//printf("pick(%d,%d)\n",ver+1,type);
	int xb;
	if(type==-1) type=(dp[ver][0]>dp[ver][1]?0:1);
	for(xb=0;xb<son[ver].size();++xb){
		if(type==0){
			pick(son[ver][xb].first,-1);
		//	printf("	Go type 0\n");
		}
		else if(son[ver][xb].first!=pf[0][ver])
			pick(son[ver][xb].first,-1);
		else{
	//		printf("\tadd\n");
			as1.push_back(mpr(ver+1,son[ver][xb].first+1));
			pick(son[ver][xb].first,0);
		}
	}
	son[ver].clear();
}
void dfs2(int ver,int prev){
	//printf("dfs2(%d,%d)\n",ver+1,prev+1);
	vis2[ver]=1;
	int xb;
	pii addon=mpr(-iinf,-iinf);
	bool vs=0;
	for(xb=0;xb<adj[ver].size();++xb){
		if(adj[ver][xb].first!=prev && ext[adj[ver][xb].first]){
			vs=1;
			son[ver].push_back(adj[ver][xb]);
			dfs2(adj[ver][xb].first,ver);
			dp2[ver][0]=dp2[ver][0]+MAX(dp2[adj[ver][xb].first][1],dp2[adj[ver][xb].first][0]);
			if(mpr(1,adj[ver][xb].second)+dp2[adj[ver][xb].first][0]-MAX(dp2[adj[ver][xb].first][1],dp2[adj[ver][xb].first][0])>addon){
				addon=mpr(1,adj[ver][xb].second)+dp2[adj[ver][xb].first][0]-MAX(dp2[adj[ver][xb].first][1],dp2[adj[ver][xb].first][0]);
				pf[1][ver]=adj[ver][xb].first;
			}
		}
	}
	if(!vs){
		dp2[ver][0]=dp2[ver][1]=mpr(0,0);
	}
	else{
		dp2[ver][1]=dp2[ver][0]+addon;
	}
	//printf("dp2[%d]:{%d,%d ; %d,%d}\n",ver+1,dp2[ver][0].first,dp2[ver][0].second,dp2[ver][1].first,dp2[ver][1].second);
}
void pick2(int ver,int type){
	//printf("pick2(%d,%d)\n",ver+1,type);
	int xb;
	if(type==-1) type=(dp2[ver][0]>dp2[ver][1]?0:1);
	for(xb=0;xb<son[ver].size();++xb){
		if(type==0)
			pick2(son[ver][xb].first,-1);
		else if(son[ver][xb].first!=pf[1][ver])
			pick2(son[ver][xb].first,-1);
		else{
	//		printf("\tadd\n");
			as2.push_back(mpr(ver+1,son[ver][xb].first+1));
			pick2(son[ver][xb].first,0);
		}
	}
	son[ver].clear();
}
int main(){
    // inputing start
    scanf("%d",&n);
	int xb,x2;
	for(xb=0;xb<n;++xb){
		scanf("%d%d",f+xb,s+xb);
		--f[xb];
	}
    #ifdef LOCAL
        TStart();
    #endif
    // calculation start
	pii fres=mpr(0,0);
    for(xb=0;xb<n;++xb){
		if(s[f[xb]]!=s[xb]){
			adj[xb].push_back(mpr(f[xb],1));
			adj[f[xb]].push_back(mpr(xb,1));
		}
		else{
			adj[xb].push_back(mpr(f[xb],0));
			adj[f[xb]].push_back(mpr(xb,0));
		}
	}
	cu=-1;
	fill(ext,ext+n,1);
	for(xb=0;xb<n;++xb){
		if(!vis[xb]){
			cu=cv=-1;
			cc=0;
			//printf("Main stage start\n");
			as1.clear();
			as2.clear();
			cur.clear();
			dfs(xb,-1);
			pick(xb,-1);
			//printf("Main stage end\n");
			pii curs=MAX(dp[xb][0],dp[xb][1]);
			ext[cu]=ext[cv]=0;
			pii ncr=mpr(0,0);
			for(x2=0;x2<cur.size();++x2){
				if(!vis2[cur[x2]] && ext[cur[x2]]){
			//		printf("Substage start\n");
					dfs2(cur[x2],-1);
					pick2(cur[x2],-1);
			//		printf("Substage end\n");
					ncr=ncr+MAX(dp2[cur[x2]][0],dp2[cur[x2]][1]);
				}
			}
			ncr=ncr+mpr(1,cc);
			as2.push_back(mpr(cu+1,cv+1));
			if(ncr>curs){
				fres=fres+ncr;
				fstp.push_back(as2);
			}
			else{
				fres=fres+curs;
				fstp.push_back(as1);
			}
		}
	}
	printf("%d %d\n",fres.first,fres.second);
	for(xb=0;xb<fstp.size();++xb){
		for(x2=0;x2<fstp[xb].size();++x2){
			printf("%d %d\n",fstp[xb][x2].first,fstp[xb][x2].second);
		}
	}
    #ifdef LOCAL
        TReport();
    #endif
    return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#define lc p*2+1
#define rc p*2+2

typedef long long ll;
typedef pair<int,int> pi;

const int Maxn=1e5+5;

bool vis[Maxn],lp[Maxn];
int bf[Maxn],sx[Maxn],stk[Maxn];
vector<int> adj[Maxn];
vector<pi> ansp,cansp[2];
int dp1[Maxn][2];
int dp2[Maxn][2];
int ps[Maxn][2];
int croot,cans[2],cp[2],flag;
int ans,p;
int n;

void findloop(int now){
	stk[now]=1;
	for(auto v:adj[now]){
		if(stk[v]==1){
			lp[v]=true;
		}
		else findloop(v);
	}
	stk[now]=2;
}

void dfs1(int now){
	vis[now]=true;
	dp1[now][0]=dp1[now][1]=0;
	int rez=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs1(v);
			dp1[now][0]+=max(dp1[v][0],dp1[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			rez=max(rez,dp1[now][0]-max(dp1[v][0],dp1[v][1])+dp1[v][0]+1);
		}
	}
	dp1[now][1]=rez;
}

void dfs2(int now){
	dp2[now][0]=dp2[now][1]=0;
	int rez1=0,rez2=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs2(v);
			if(dp1[v][0]>dp1[v][1]) dp2[now][0]+=dp2[v][0];
			else if(dp1[v][0]<dp1[v][1]) dp2[now][0]+=dp2[v][1];
			else dp2[now][0]+=max(dp2[v][0],dp2[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			int x;
			if(dp1[v][0]>dp1[v][1]){
				rez1=dp1[now][0]+1;
				x=dp2[now][0]+(sx[now]==sx[v]?0:1);
			}
			else if(dp1[v][0]<dp1[v][1]){
				rez1=dp1[now][0]-dp1[v][1]+dp1[v][0]+1;
				x=dp2[now][0]-dp2[v][1]+dp2[v][0]+(sx[now]==sx[v]?0:1);
			}
			else{
				rez1=dp1[now][0]+1;
				x=dp2[now][0]-max(dp2[v][0],dp2[v][1])+dp2[v][0]+(sx[now]==sx[v]?0:1);
			}
			if(rez1==dp1[now][1]){
				if(rez2<=x){
					rez2=x;
					ps[now][flag]=v;
				}
			}
		}
	}
	dp2[now][1]=rez2;
}

void dfs3(int now){
	if(dp1[now][0]>dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	if(dp1[now][0]<dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	if(dp2[now][0]>dp2[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	else{
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	return;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",bf+i,sx+i);
		adj[bf[i]].push_back(i);
	}
	memset(vis,0,sizeof vis);
	memset(stk,0,sizeof stk);
	for(int i=1;i<=n;i++){
		if(!stk[i])findloop(i);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]&&lp[i]){
			//cout<<"this is "<<i<<" "<<bf[i]<<endl;
			flag=0;
			croot=i;
			dfs1(i);
			dfs2(i);
			dfs3(i);
			if(dp1[i][0]>dp1[i][1]){
				cans[flag]=dp1[i][0];
				cp[flag]=dp2[i][0];
			}
			else if(dp1[i][0]<dp1[i][1]){
				cans[flag]=dp1[i][1];
				cp[flag]=dp2[i][1];
			}
			else{
				cans[flag]=dp1[i][0];
				cp[flag]=max(dp2[i][0],dp2[i][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][0]<<" ";cout<<endl;
			croot=bf[i];
			flag=1;
			dfs1(bf[i]);
			dfs2(bf[i]);
			dfs3(bf[i]);
			if(dp1[bf[i]][0]>dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=dp2[bf[i]][0];
			}
			else if(dp1[bf[i]][0]<dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][1];
				cp[flag]=dp2[bf[i]][1];
			}
			else{
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=max(dp2[bf[i]][0],dp2[bf[i]][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][1]<<" ";cout<<endl;
			if(cans[0]>cans[1]){
				ans+=cans[0];
				for(auto v:cansp[0]){
					if(sx[v.first]!=sx[v.second]) p++;
					ansp.push_back(v);
				}
			}
			else if(cans[0]<cans[1]){
				ans+=cans[1];
				for(auto v:cansp[1]){
					if(sx[v.first]!=sx[v.second]) p++;
					ansp.push_back(v);
				}
			}
			else if(cp[0]>=cp[1]){
				ans+=cans[0];
				for(auto v:cansp[0]){
					if(sx[v.first]!=sx[v.second]) p++;
					ansp.push_back(v);
				}
			}
			else{
				ans+=cans[1];
				for(auto v:cansp[1]){
					if(sx[v.first]!=sx[v.second]) p++;
					ansp.push_back(v);
				}
			}
			cansp[0].clear();
			cansp[1].clear();
		}
	}
	printf("%d %d\n",ans,p);
	for(auto v:ansp){
		printf("%d %d\n",v.first,v.second);
	}
return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#define lc p*2+1
#define rc p*2+2

typedef long long ll;
typedef pair<int,int> pi;

const int Maxn=1e5+5;

bool vis[Maxn],lp[Maxn];
int bf[Maxn],sx[Maxn],stk[Maxn];
vector<int> adj[Maxn];
vector<pi> ansp,cansp[2];
int dp1[Maxn][2];
int dp2[Maxn][2];
int ps[Maxn][2];
int croot,cans[2],cp[2],flag;
int ans,p;
int n;

void findloop(int now){
	stk[now]=1;
	for(auto v:adj[now]){
		if(stk[v]==1){
			lp[v]=true;
		}
		else findloop(v);
	}
	stk[now]=2;
}

void dfs1(int now){
	vis[now]=true;
	dp1[now][0]=dp1[now][1]=0;
	int rez=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs1(v);
			dp1[now][0]+=max(dp1[v][0],dp1[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			rez=max(rez,dp1[now][0]-max(dp1[v][0],dp1[v][1])+dp1[v][0]+1);
		}
	}
	dp1[now][1]=rez;
}

void dfs2(int now){
	dp2[now][0]=dp2[now][1]=0;
	int rez1=0,rez2=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs2(v);
			if(dp1[v][0]>dp1[v][1]) dp2[now][0]+=dp2[v][0];
			else if(dp1[v][0]<dp1[v][1]) dp2[now][0]+=dp2[v][1];
			else dp2[now][0]+=max(dp2[v][0],dp2[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			int x;
			if(dp1[v][0]>dp1[v][1]){
				rez1=dp1[now][0]+1;
				x=dp2[now][0]+(sx[now]==sx[v]?0:1);
			}
			else if(dp1[v][0]<dp1[v][1]){
				rez1=dp1[now][0]-dp1[v][1]+dp1[v][0]+1;
				x=dp2[now][0]-dp2[v][1]+dp2[v][0]+(sx[now]==sx[v]?0:1);
			}
			else{
				rez1=dp1[now][0]+1;
				x=dp2[now][0]-max(dp2[v][0],dp2[v][1])+dp2[v][0]+(sx[now]==sx[v]?0:1);
			}
			if(rez1==dp1[now][1]){
				if(rez2<=x){
					rez2=x;
					ps[now][flag]=v;
				}
			}
		}
	}
	dp2[now][1]=rez2;
}

void dfs3(int now){
	if(dp1[now][0]>dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	if(dp1[now][0]<dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	if(dp2[now][0]>dp2[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	else{
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	return;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",bf+i,sx+i);
		adj[bf[i]].push_back(i);
	}
	memset(vis,0,sizeof vis);
	memset(stk,0,sizeof stk);
	for(int i=1;i<=n;i++){
		if(!stk[i])findloop(i);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]&&lp[i]){
			//cout<<"this is "<<i<<" "<<bf[i]<<endl;
			flag=0;
			croot=i;
			dfs1(i);
			dfs2(i);
			dfs3(i);
			if(dp1[i][0]>dp1[i][1]){
				cans[flag]=dp1[i][0];
				cp[flag]=dp2[i][0];
			}
			else if(dp1[i][0]<dp1[i][1]){
				cans[flag]=dp1[i][1];
				cp[flag]=dp2[i][1];
			}
			else{
				cans[flag]=dp1[i][0];
				cp[flag]=max(dp2[i][0],dp2[i][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][0]<<" ";cout<<endl;
			croot=bf[i];
			flag=1;
			dfs1(bf[i]);
			dfs2(bf[i]);
			dfs3(bf[i]);
			if(dp1[bf[i]][0]>dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=dp2[bf[i]][0];
			}
			else if(dp1[bf[i]][0]<dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][1];
				cp[flag]=dp2[bf[i]][1];
			}
			else{
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=max(dp2[bf[i]][0],dp2[bf[i]][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][1]<<" ";cout<<endl;
			if(cans[0]>cans[1]){
				ans+=cans[0];
				p+=cp[0];
				for(auto v:cansp[0]){
					ansp.push_back(v);
				}
			}
			else if(cans[0]<cans[1]){
				ans+=cans[1];
				p+=cp[1];
				for(auto v:cansp[1]){
					ansp.push_back(v);
				}
			}
			else if(cp[0]>cp[1]){
				ans+=cans[0];
				p+=cp[0];
				for(auto v:cansp[0]){
					ansp.push_back(v);
				}
			}
			else{
				ans+=cans[1];
				p+=cp[1];
				for(auto v:cansp[1]){
					ansp.push_back(v);
				}
			}
			cansp[0].clear();
			cansp[1].clear();
		}
	}
	printf("%d %d\n",ans,p);
	for(auto v:ansp){
		printf("%d %d\n",v.first,v.second);
	}
return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#define lc p*2+1
#define rc p*2+2

typedef long long ll;
typedef pair<int,int> pi;

const int Maxn=1e5+5;

bool vis[Maxn],lp[Maxn];
int bf[Maxn],sx[Maxn],stk[Maxn];
vector<int> adj[Maxn];
vector<pi> ansp,cansp[2];
int dp1[Maxn][2];
int dp2[Maxn][2];
int ps[Maxn][2];
int croot,cans[2],cp[2],flag;
int ans,p;
int n;

void findloop(int now){
	stk[now]=1;
	for(auto v:adj[now]){
		if(stk[v]==1){
			lp[v]=true;
		}
		else findloop(v);
	}
	stk[now]=2;
}

void dfs1(int now){
	vis[now]=true;
	dp1[now][0]=dp1[now][1]=0;
	int rez=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs1(v);
			dp1[now][0]+=max(dp1[v][0],dp1[v][1]);
			dp1[now][1]+=max(dp1[v][0],dp1[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			rez=max(rez,dp1[now][1]-max(dp1[v][0],dp1[v][1])+dp1[v][0]+1);
		}
	}
	dp1[now][1]=rez;
}

void dfs2(int now){
	dp2[now][0]=dp2[now][1]=0;
	int rez1=0,rez2=0;
	for(auto v:adj[now]){
		if(v!=croot){
			dfs2(v);
			dp2[now][0]+=max(dp2[v][0],dp2[v][1]);
			dp2[now][1]+=max(dp2[v][0],dp2[v][1]);
			rez2+=max(dp1[v][0],dp1[v][1]);
		}
	}
	for(auto v:adj[now]){
		if(v!=croot){
			if(rez2-max(dp1[v][0],dp1[v][1])+dp1[v][0]+1==dp1[now][1]){
				int x=dp2[now][1]-max(dp2[v][0],dp2[v][1])+dp2[v][0]+(sx[now]==sx[v]?0:1);
				if(rez1<=x){
					rez1=x;
					ps[now][flag]=v;
				}
			}
		}
	}
	dp2[now][1]=rez1;
}

void dfs3(int now){
	if(dp1[now][0]>dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	if(dp1[now][0]<dp1[now][1]){
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	if(dp2[now][0]>dp2[now][1]){
		for(auto v:adj[now]){
			if(v!=croot)dfs3(v);
		}
		return;
	}
	else{
		for(auto v:adj[now]){
			if(v!=croot){
				if(v==ps[now][flag]){
					cansp[flag].push_back(pi(now,v));
					for(auto u:adj[v]){
						if(u!=croot) dfs3(u);
					}
				}
				else dfs3(v);
			}
		}
		return;
	}
	return;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",bf+i,sx+i);
		adj[bf[i]].push_back(i);
	}
	memset(vis,0,sizeof vis);
	memset(stk,0,sizeof stk);
	for(int i=1;i<=n;i++){
		if(!stk[i])findloop(i);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]&&lp[i]){
			//cout<<"this is "<<i<<" "<<bf[i]<<endl;
			flag=0;
			croot=i;
			dfs1(i);
			dfs2(i);
			dfs3(i);
			if(dp1[i][0]>dp1[i][1]){
				cans[flag]=dp1[i][0];
				cp[flag]=dp2[i][0];
			}
			else if(dp1[i][0]<dp1[i][1]){
				cans[flag]=dp1[i][1];
				cp[flag]=dp2[i][1];
			}
			else{
				cans[flag]=dp1[i][0];
				cp[flag]=max(dp2[i][0],dp2[i][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][0]<<" ";cout<<endl;
			croot=bf[i];
			flag=1;
			dfs1(bf[i]);
			dfs2(bf[i]);
			dfs3(bf[i]);
			if(dp1[bf[i]][0]>dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=dp2[bf[i]][0];
			}
			else if(dp1[bf[i]][0]<dp1[bf[i]][1]){
				cans[flag]=dp1[bf[i]][1];
				cp[flag]=dp2[bf[i]][1];
			}
			else{
				cans[flag]=dp1[bf[i]][0];
				cp[flag]=max(dp2[bf[i]][0],dp2[bf[i]][1]);
			}
			//for(int k=1;k<=n;k++)cout<<dp1[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp1[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][0]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<dp2[k][1]<<" ";cout<<endl;
			//for(int k=1;k<=n;k++)cout<<ps[k][1]<<" ";cout<<endl;
			if(cans[0]>cans[1]){
				ans+=cans[0];
				p+=cp[0];
				for(auto v:cansp[0]) ansp.push_back(v);
			}
			else if(cans[0]<cans[1]){
				ans+=cans[1];
				p+=cp[1];
				for(auto v:cansp[1]) ansp.push_back(v);
			}
			else if(cp[0]>=cp[1]){
				ans+=cans[0];
				p+=cp[0];
				for(auto v:cansp[0]) ansp.push_back(v);
			}
			else{
				ans+=cans[1];
				p+=cp[1];
				for(auto v:cansp[1]) ansp.push_back(v);
			}
			cansp[0].clear();
			cansp[1].clear();
		}
	}
	printf("%d %d\n",ans,p);
	for(auto v:ansp){
		printf("%d %d\n",v.first,v.second);
	}
return 0;
}