#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <stdio.h>
#include <queue>
#include <set>
#include <list>
#include <cmath>
#include <assert.h>
#include <bitset>
#include <cstring>
#include <map>
#include <iomanip> //cout << setprecision(node) << fixed << num
#include <stack>
#include <sstream>

#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define print(arr) for (auto it = arr.begin(); it != arr.end(); ++it) cout << *it << " "; cout << endl;
#define debug(x) cout << x << endl;
#define debug2(x,y) cout << x << " " << y << endl;
#define debug3(x,y,z) cout << x << " " << y << " " << z << endl;

typedef long long int ll;
typedef long double ld;
typedef unsigned long long int ull;
typedef std::pair <int, int> ii;
typedef std::vector <int> vi;
typedef std::vector <ll> vll;
typedef std::vector <ld> vld;

const int INF = int(1e9);
const ll INF64 = ll(1e18);
const ld EPS = 1e-9, PI = 3.1415926535897932384626433832795;
using namespace std;

const int maxn = (int) 1e5+7;
vi tree[maxn], graph[maxn], comp;
vector <ii> pairs;
int gender[maxn], bff[maxn], dp[maxn][2], dp2[maxn][2], parent[maxn], N;
bool visited[maxn];
ii nxt[maxn][2];

void dfs(int node, int par) {
    dp[node][0] = dp[node][1] = dp2[node][0] = dp2[node][1] = 0;
    nxt[node][1] = mp(0, 0);
    for (int adj : tree[node]) {
        if (adj != par) {
            dfs(adj, node);
            dp[node][1] += dp[adj][0];
            dp2[node][1] += dp2[adj][0];
        }
    }
    for (int adj : tree[node]) {
        if (adj != par) {
            int tmp = dp[adj][1] + 1 + dp[node][1] - dp[adj][0];
            if (dp[node][0] < tmp) {
                dp[node][0] = tmp;
                dp2[node][0] = dp2[adj][1] + (gender[node] != gender[adj]) + dp2[node][1] - dp2[adj][0];
                nxt[node][0] = mp(adj, 1);
            }
            else if (dp[node][0] == tmp) {
                tmp = dp2[adj][1] + (gender[node] != gender[adj]) + dp2[node][1] - dp2[adj][0];
                if (tmp > dp2[node][0]) {
                    dp2[node][0] = tmp;
                    nxt[node][0] = mp(adj, 1);
                }
            }
        }
    }
    //debug3(node, dp[node][0], dp2[node][0]);
    //debug3(node, dp[node][1], dp2[node][1]);
    if (dp[node][1] > dp[node][0]) {
        dp[node][0] = dp[node][1];
        dp2[node][0] = dp2[node][1];
        nxt[node][0] = {0, 1};
    }
    else if (dp[node][1] == dp[node][0]) {
        if (dp2[node][1] > dp2[node][0]) {
            dp2[node][0] = dp2[node][1];
            nxt[node][0] = {0, 1};
        }
    }
}
vector <ii> pairings, final_pairings;

void dfs2(int node, int par, int type) {
    ii nxt_pair = nxt[node][type];
    if (type == 1 && par != 0) {
        pairings.pb(mp(node, par));
    }
    for (int adj : tree[node]) {
        if (adj != par) {
            if (adj == nxt_pair.fi) {
                dfs2(adj, node, 1);
            }
            else {
                dfs2(adj, node, 0);
            }
        }
    }
}

void dfsFindComponents(int node) {
    visited[node] = true;
    comp.pb(node);
    for (int adj : graph[node]) {
        if (!visited[adj]) {
            dfsFindComponents(adj);
        }
    }
}

int root(int x) {
    while (parent[x] != x) {
        parent[x] = parent[parent[x]];
        x = parent[x];
    }
    return x;
}

inline void Union(int a, int b) {
    a = root(a), b = root(b);
    parent[a] = b;
}

inline bool Find(int a, int b) {
    return root(a) == root(b);
}

ii solve(vi &component) {

    int start = 0;
    ii problem_edge = mp(-1, -1);
    set <ii> edges;
    for (int n : component) {
        if (Find(n, bff[n])) {
            assert(problem_edge == mp(-1, -1));
            problem_edge = mp(n, bff[n]);
        }
        else {
            Union(n, bff[n]);
            int u = n, v = bff[n];
            if (u > v) swap(u, v);
            edges.insert(mp(u, v));
            if (!start) start = u;
            tree[u].pb(v);
            tree[v].pb(u);
        }
    }

    //for (int i = 1; i <= N; i++) {cout << i << ": "; print(tree[i]);}
    pairings.clear();

    dfs(start, 0);
    int t = dp[start][0], e = dp2[start][0];
    dfs2(start, 0, 0);

    //create second graph
    start = 0;
    int u = problem_edge.se, v = bff[problem_edge.se];
    if (u > v) swap(u, v);
    edges.erase(mp(u, v));
    edges.insert(problem_edge);
    for (int n : component) {
        tree[n].clear();
        tree[bff[n]].clear();
    }
    for (ii edge : edges) {
        if (!start) start = edge.fi;
        tree[edge.fi].pb(edge.se);
        tree[edge.se].pb(edge.fi);
    }
    //second graph made

    //for (int i = 1; i <= N; i++) {cout << i << ": "; print(tree[i]);}

    dfs(start, 0);
    if (dp[start][0] > t) {
        t = dp[start][0];
        e = dp2[start][0];
        pairings.clear();
        dfs2(start, 0, 0);
    }
    else if (dp[start][0] == t && dp2[start][0] > e) {
        e = dp2[start][0];
        pairings.clear();
        dfs2(start, 0, 0);
    }

    for (ii p : pairings) {
        final_pairings.pb(p);
    }
    return mp(t, e);
}

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        int v;
        scanf("%d %d", &v, &gender[i]);
        pairs.pb(mp(i, v));
        graph[i].pb(v);
        graph[v].pb(i);
        bff[i] = v;
        parent[i] = i;
    }
    vector <vi> components;
    for (int i = 1; i <= N; i++) {
        if (!visited[i]) {
            comp.clear();
            dfsFindComponents(i);
            components.pb(comp);
        }
    }
    memset(visited, false, sizeof(visited));
    int t = 0, e = 0;
    for (int i = 0; i < components.size(); i++) {
        ii ret = solve(components[i]);
        t += ret.fi, e += ret.se;
    }
    printf("%d %d\n", t, e);
    for (ii p : final_pairings) {
        printf("%d %d\n", p.fi, p.se);
    }
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <stdio.h>
#include <queue>
#include <set>
#include <list>
#include <cmath>
#include <assert.h>
#include <bitset>
#include <cstring>
#include <map>
#include <iomanip> //cout << setprecision(node) << fixed << num
#include <stack>
#include <sstream>

#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define print(arr) for (auto it = arr.begin(); it != arr.end(); ++it) cout << *it << " "; cout << endl;
#define debug(x) cout << x << endl;
#define debug2(x,y) cout << x << " " << y << endl;
#define debug3(x,y,z) cout << x << " " << y << " " << z << endl;

typedef long long int ll;
typedef long double ld;
typedef unsigned long long int ull;
typedef std::pair <int, int> ii;
typedef std::vector <int> vi;
typedef std::vector <ll> vll;
typedef std::vector <ld> vld;

const int INF = int(1e9);
const ll INF64 = ll(1e18);
const ld EPS = 1e-9, PI = 3.1415926535897932384626433832795;
using namespace std;

const int maxn = (int) 1e5+7;
vi tree[maxn], graph[maxn], comp;
vector <ii> pairs;
int gender[maxn], bff[maxn], dp[maxn][2], dp2[maxn][2], parent[maxn], N;
bool visited[maxn];
ii nxt[maxn][2];

void dfs(int node, int par) {
    dp[node][0] = dp[node][1] = dp2[node][0] = dp2[node][1] = 0;
    nxt[node][1] = mp(0, 0);
    for (int adj : tree[node]) {
        if (adj != par) {
            dfs(adj, node);
            dp[node][1] += dp[adj][0];
            dp2[node][1] += dp2[adj][0];
        }
    }
    for (int adj : tree[node]) {
        if (adj != par) {
            int tmp = dp[adj][1] + 1 + dp[node][1] - dp[adj][0];
            if (dp[node][0] < tmp) {
                dp[node][0] = tmp;
                dp2[node][0] = dp2[adj][1] + (gender[node] != gender[adj]) + dp2[node][1] - dp2[adj][0];
                nxt[node][0] = mp(adj, 1);
            }
            else if (dp[node][0] == tmp) {
                tmp = dp2[node][0] = dp2[adj][1] + (gender[node] != gender[adj]) + dp2[node][1] - dp2[adj][0];
                if (tmp > dp2[node][0]) {
                    dp2[node][0] = tmp;
                    nxt[node][0] = mp(adj, 1);
                }
            }
        }
    }
    //debug3(node, dp[node][0], dp2[node][0]);
    //debug3(node, dp[node][1], dp2[node][1]);
    if (dp[node][1] > dp[node][0]) {
        dp[node][0] = dp[node][1];
        dp2[node][0] = dp2[node][1];
        nxt[node][0] = {0, 1};
    }
    else if (dp[node][1] == dp[node][0]) {
        if (dp2[node][1] > dp2[node][0]) {
            dp2[node][0] = dp2[node][1];
            nxt[node][0] = {0, 1};
        }
    }
}
vector <ii> pairings, final_pairings;

void dfs2(int node, int par, int type) {
    ii nxt_pair = nxt[node][type];
    if (type == 1 && par != 0) {
        pairings.pb(mp(node, par));
    }
    for (int adj : tree[node]) {
        if (adj != par) {
            if (adj == nxt_pair.fi) {
                dfs2(adj, node, 1);
            }
            else {
                dfs2(adj, node, 0);
            }
        }
    }
}

void dfsFindComponents(int node) {
    visited[node] = true;
    comp.pb(node);
    for (int adj : graph[node]) {
        if (!visited[adj]) {
            dfsFindComponents(adj);
        }
    }
}

int root(int x) {
    while (parent[x] != x) {
        parent[x] = parent[parent[x]];
        x = parent[x];
    }
    return x;
}

inline void Union(int a, int b) {
    a = root(a), b = root(b);
    parent[a] = b;
}

inline bool Find(int a, int b) {
    return root(a) == root(b);
}

ii solve(vi &component) {

    int start = 0;
    ii problem_edge = mp(-1, -1);
    set <ii> edges;
    for (int n : component) {
        if (Find(n, bff[n])) {
            assert(problem_edge == mp(-1, -1));
            problem_edge = mp(n, bff[n]);
        }
        else {
            Union(n, bff[n]);
            int u = n, v = bff[n];
            if (u > v) swap(u, v);
            edges.insert(mp(u, v));
            if (!start) start = u;
            tree[u].pb(v);
            tree[v].pb(u);
        }
    }

    //for (int i = 1; i <= N; i++) {cout << i << ": "; print(tree[i]);}
    pairings.clear();

    dfs(start, 0);
    int t = dp[start][0], e = dp2[start][0];
    dfs2(start, 0, 0);

    //create second graph
    start = 0;
    int u = problem_edge.se, v = bff[problem_edge.se];
    if (u > v) swap(u, v);
    edges.erase(mp(u, v));
    edges.insert(problem_edge);
    for (int n : component) {
        tree[n].clear();
        tree[bff[n]].clear();
    }
    for (ii edge : edges) {
        if (!start) start = edge.fi;
        tree[edge.fi].pb(edge.se);
        tree[edge.se].pb(edge.fi);
    }
    //second graph made

    //for (int i = 1; i <= N; i++) {cout << i << ": "; print(tree[i]);}

    dfs(start, 0);
    if (dp[start][0] > t) {
        t = dp[start][0];
        e = dp2[start][0];
        pairings.clear();
        dfs2(start, 0, 0);
    }
    else if (dp[start][0] == t && dp2[start][0] > e) {
        e = dp2[start][0];
        pairings.clear();
        dfs2(start, 0, 0);
    }

    for (ii p : pairings) {
        final_pairings.pb(p);
    }
    return mp(t, e);
}

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        int v;
        scanf("%d %d", &v, &gender[i]);
        pairs.pb(mp(i, v));
        graph[i].pb(v);
        graph[v].pb(i);
        bff[i] = v;
        parent[i] = i;
    }
    vector <vi> components;
    for (int i = 1; i <= N; i++) {
        if (!visited[i]) {
            comp.clear();
            dfsFindComponents(i);
            components.pb(comp);
        }
    }
    memset(visited, false, sizeof(visited));
    int t = 0, e = 0;
    for (int i = 0; i < components.size(); i++) {
        ii ret = solve(components[i]);
        t += ret.fi, e += ret.se;
    }
    printf("%d %d\n", t, e);
    for (ii p : final_pairings) {
        printf("%d %d\n", p.fi, p.se);
    }
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long int
ll n;
vector<ll> g[100010];
bool found=0;
bool sex[100010];
ll frnd[100010];
ll x1,x2,x3;
map<pair<ll,ll>,ll> m1;
ll offset = 1000000;
ll nxt=1;
ll dp[1000000];
vector<pair<ll,ll> > op;
void DFSH(ll id,bool visited[],ll p){
	visited[id]=1;
	for(auto v:g[id]){
		if(visited[v]==1 && v!=p && found==0){
			x1=p;
			x2=id;
			x3=v;
			found=1;
//			return;
		}
		if(visited[v]==0)
			DFSH(v,visited,id);
//		if(found==1)
//			return;
	}
}

ll fun(ll u,ll p){
	//cout<<u<<" "<<p<<endl;
	ll index=m1[make_pair(u,p)];
	if(index!=0){
		return dp[index];
	}
	ll a1=0;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			a1+=fun(v,u);
		}
	}
	ll a2;
	ll mx=a1;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			if(sex[u]!=sex[v])
				a2=offset+1;
			else
				a2=offset;
			a2+=a1-fun(v,u);
			for(auto v1:g[v]){
				if(v1!=u && v1!=-1){
					a2+=fun(v1,v);
				}
			}
			mx=max(mx,a2);
		}
	}
	m1[make_pair(u,p)]=nxt;
	dp[nxt]=mx;
	nxt++;
	return mx;
}
ll pfun(ll u,ll p){
	//cout<<u<<" "<<p<<endl;
	ll index=m1[make_pair(u,p)];
	ll xyz=dp[index];
	ll a1=0;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			a1+=fun(v,u);
		}
	}
	if(xyz==a1){
		for(auto v:g[u]){
			if(v!=p && v!=-1){
				pfun(v,u);
			}
		}
		return dp[index];
	}
	ll a2;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			if(sex[u]!=sex[v])
				a2=offset+1;
			else
				a2=offset;
			a2+=a1-fun(v,u);
			for(auto v1:g[v]){
				if(v1!=u && v1!=-1){
					a2+=fun(v1,v);
				}
			}
			if(a2==xyz){
				op.push_back(make_pair(u+1,v+1));
				//cout<<u+1<<" "<<v+1<<endl;
				for(auto v1:g[u]){
					if(v1!=p && v1!=-1 && v1!=v){
						pfun(v1,u);
					}
				}
				for(auto v1:g[v]){
					if(v1!=u && v1!=-1){
						a2+=pfun(v1,v);
					}
				}
				return dp[index];
			}
		}
	}
	return dp[index];
}
void solve(){
	ll ans=0,final=0;
	bool visited[100010]={0};
	for(int i=0;i<n;i++){
		if(visited[i]==1)
			continue;
		//cout<<"here : "<<i<<endl;
		found=0;
		DFSH(i,visited,-1);
		ans=0;
		//cout<<found<<endl;
	//	cout<<x1<<x2<<x3<<endl;
		if(found==1){
			for(auto &v:g[x1]){
				if(v==x2)
				{
					v=-1;
					break;
				}
			}
			for(auto &v:g[x2]){
				if(v==x1)
				{
					v=-1;
					break;
				}
			}
			//for(auto &v:g[x2]){
			//	cout<<v<<endl;
			//}
			//
			m1.clear();
			ans=max(fun(i,-1),ans);
			m1.clear();
			//cout<<ans<<endl;
			//
			for(auto &v:g[x1]){
				if(v==-1)
				{
					v=x2;
					break;
				}
			}
			for(auto &v:g[x2]){
				if(v==-1)
				{
					v=x1;
				}
				else if(v==x3){
					v=-1;
				}
			}
			for(auto &v:g[x3]){
				if(v==x2){
					v=-1;
				}
			}
			ans=max(ans,fun(i,-1));

			//
	//		cout<<ans<<" "<<fun(i,-1)<<endl;
			if(ans==fun(i,-1)){
				pfun(i,-1);
			}				//
			else{
				for(auto &v:g[x3]){
					if(v==-1)
					{
						v=x2;
						break;
					}
				}
				for(auto &v:g[x2]){
					if(v==-1)
					{
						v=x3;
					}
					else if(v==x1){
						v=-1;
					}
				}
				for(auto &v:g[x1]){
					if(v==x2){
						v=-1;
					}
				}
				m1.clear();
				fun(i,-1);
				pfun(i,-1);
			}
			//

		}
		else{
			m1.clear();
			ans=max(ans,fun(i,-1));
			pfun(i,-1);
		}
		final+=ans;
		//cout<<fun(0,-1)<<endl;

		}
	cout<<final/offset<<" ";
	cout<<final%offset<<endl;
	ll sss=op.size();
	for(int i=0;i<sss;i++){
		cout<<op[i].first<<" "<<op[i].second<<endl;
	}
}
int main(){
	ll x,s;
	cin>>n;
	map<pair<ll,ll>,ll> m3;
	for(ll i=0;i<n;i++){
		cin>>x>>s;
		x--;
		s--;
		sex[i]=s;
		if(m3[make_pair(i,x)]==0 ){
			//cout<<i<<" "<<x<<endl;
			m3[make_pair(i,x)]=1;
			m3[make_pair(x,i)]=1;
			g[i].push_back(x);
			g[x].push_back(i);
		}
		frnd[i]=x;
	}
	solve();
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long int
ll n;
vector<ll> g[100010];
bool found=0;
bool sex[100010];
ll frnd[100010];
ll x1,x2,x3;
map<pair<ll,ll>,ll> m1;
ll offset = 1000000;
ll nxt=1;
ll dp[1000000];
vector<pair<ll,ll> > op;
void DFSH(ll id,bool visited[],ll p){
	visited[id]=1;
	for(auto v:g[id]){
		if(visited[v]==1 && v!=p && found==0){
			x1=p;
			x2=id;
			x3=v;
			found=1;
//			return;
		}
		if(visited[v]==0)
			DFSH(v,visited,id);
//		if(found==1)
//			return;
	}
}

ll fun(ll u,ll p){
	//cout<<u<<" "<<p<<endl;
	ll index=m1[make_pair(u,p)];
	if(index!=0){
		return dp[index];
	}
	ll a1=0;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			a1+=fun(v,u);
		}
	}
	ll a2;
	ll mx=a1;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			if(sex[u]!=sex[v])
				a2=offset+1;
			else
				a2=offset;
			a2+=a1-fun(v,u);
			for(auto v1:g[v]){
				if(v1!=u && v1!=-1){
					a2+=fun(v1,v);
				}
			}
			mx=max(mx,a2);
		}
	}
	m1[make_pair(u,p)]=nxt;
	dp[nxt]=mx;
	nxt++;
	return mx;
}
ll pfun(ll u,ll p){
	//cout<<u<<" "<<p<<endl;
	ll index=m1[make_pair(u,p)];
	ll xyz=dp[index];
	ll a1=0;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			a1+=fun(v,u);
		}
	}
	if(xyz==a1){
		for(auto v:g[u]){
			if(v!=p && v!=-1){
				pfun(v,u);
			}
		}
		return dp[index];
	}
	ll a2;
	for(auto v:g[u]){
		if(v!=p && v!=-1){
			if(sex[u]!=sex[v])
				a2=offset+1;
			else
				a2=offset;
			a2+=a1-fun(v,u);
			for(auto v1:g[v]){
				if(v1!=u && v1!=-1){
					a2+=fun(v1,v);
				}
			}
			if(a2==xyz){
				op.push_back(make_pair(u+1,v+1));
				//cout<<u+1<<" "<<v+1<<endl;
				for(auto v1:g[u]){
					if(v1!=p && v1!=-1 && v1!=v){
						pfun(v1,u);
					}
				}
				for(auto v1:g[v]){
					if(v1!=u && v1!=-1){
						a2+=pfun(v1,v);
					}
				}
			}
		}
	}
	return dp[index];
}
void solve(){
	ll ans=0,final=0;
	bool visited[100010]={0};
	for(int i=0;i<n;i++){
		if(visited[i]==1)
			continue;
		//cout<<"here : "<<i<<endl;
		found=0;
		DFSH(i,visited,-1);
		ans=0;
		//cout<<found<<endl;
		//cout<<x1<<x2<<x3<<endl;
		if(found==1){
			for(auto &v:g[x1]){
				if(v==x2)
				{
					v=-1;
					break;
				}
			}
			for(auto &v:g[x2]){
				if(v==x1)
				{
					v=-1;
					break;
				}
			}
			//for(auto &v:g[x2]){
			//	cout<<v<<endl;
			//}
			//
			m1.clear();
			ans=max(fun(i,-1),ans);
			m1.clear();
			//cout<<ans<<endl;
			//
			for(auto &v:g[x1]){
				if(v==-1)
				{
					v=x2;
					break;
				}
			}
			for(auto &v:g[x2]){
				if(v==-1)
				{
					v=x1;
				}
				else if(v==x3){
					v=-1;
				}
			}
			for(auto &v:g[x3]){
				if(v==x2){
					v=-1;
				}
			}
			ans=max(ans,fun(i,-1));

			//
			if(ans==fun(i,-1))
				pfun(i,-1);
			else{
				for(auto &v:g[x3]){
					if(v==-1)
					{
						v=x2;
						break;
					}
				}
				for(auto &v:g[x2]){
					if(v==-1)
					{
						v=x3;
					}
					else if(v==x1){
						v=-1;
					}
				}
				for(auto &v:g[x1]){
					if(v==x2){
						v=-1;
					}
				}
				m1.clear();
				fun(i,-1);
				pfun(i,-1);
			}
			//

		}
		else{
			m1.clear();
			ans=max(ans,fun(i,-1));
			pfun(i,-1);
		}
		final+=ans;
		//cout<<fun(0,-1)<<endl;

		}
	cout<<final/offset<<" ";
	cout<<final%offset<<endl;
	ll sss=op.size();
	for(int i=0;i<sss;i++){
		cout<<op[i].first<<" "<<op[i].second<<endl;
	}
}
int main(){
	ll x,s;
	cin>>n;
	map<pair<ll,ll>,ll> m3;
	for(ll i=0;i<n;i++){
		cin>>x>>s;
		x--;
		s--;
		sex[i]=s;
		if(m3[make_pair(i,x)]==0 ){
			//cout<<i<<" "<<x<<endl;
			m3[make_pair(i,x)]=1;
			m3[make_pair(x,i)]=1;
			g[i].push_back(x);
			g[x].push_back(i);
		}
		frnd[i]=x;
	}
	solve();
	return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2|| in_chain[v])continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                stk.pop();
                if (chain.back() == v)break;
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1)dfs_ans(now,0,mans);
    else{
        if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
        else dfs_ans(now,0,mans);
    }
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1)dfs_ans(now,0,mans);
        else{
            if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
            else dfs_ans(now,0,mans);
        }
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2|| in_chain[v])continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                stk.pop();
                if (chain.back() == v)break;
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else{
        if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
        else dfs_ans(now,0,mans);
    }
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else{
            if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
            else dfs_ans(now,0,mans);
        }
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        if (pre){
            pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
            if (temp_dp >DP[x][1]){
                DP[x][1] = temp_dp;
                used[x] = 1;
            }
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2|| in_chain[v])continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                stk.pop();
                if (chain.back() == v)break;
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

/*    cout<<"chain: ";
    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/
    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
   // cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
    //cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                stk.pop();
                if (chain.back() == v)break;
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else{
        if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
        else dfs_ans(now,0,mans);
    }
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else{
            if (dp[now][1] > dp[now][0])dfs_ans(now,1,mans);
            else dfs_ans(now,0,mans);
        }
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
    if (pre[u] == 0)dp[u][1] = make_pair(0,0);
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        if (pre){
            pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
            if (temp_dp >DP[x][1]){
                DP[x][1] = temp_dp;
                used[x] = 1;
            }
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    pair<int,int> max_del = make_pair(-inf,-inf);
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        if (del > max_del){
            max_del = del;
            pre[u] = v;
        }
    }
    dp[u][1] = dp[u][1] +  max_del;
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    for (int x : chain)used[x] = 0;
    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        if (pre){
            pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair(1,(int)(color[x] != color[pre]));
            if (temp_dp >DP[x][1]){
                DP[x][1] = temp_dp;
                used[x] = 1;
            }
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
        if (temp_dp >DP[x][1]){
            DP[x][1] = temp_dp;
            used[x] = 1;
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 1;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        pair<int,int> temp_dp = dp[u][0] + del;
        if (temp_dp > dp[u][1]){
            dp[u][1] = temp_dp;
            pre[u] = v;
        }
    }
    if (pre[u] == 0)dp[u][1] = make_pair(-1,0);
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        if (pre){
            pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
            if (temp_dp >DP[x][1]){
                DP[x][1] = temp_dp;
                used[x] = 1;
            }
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


/* Generated by powerful Codeforces Tool
 * Author: calabash_boy_love_15
 * Time: 2019-10-09 14:01:02
 * Personal Code Template: https://github.com/4thcalabash/ACM-Code-Library
**/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[91m[%s %3d]: " fmt "\n\033[0m", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0)
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
/************* header ******************/

const int maxn = 1e5 + 100;
vector<int> E[maxn];
vector<int> chain;
int color[maxn];
int vis[maxn];
int n;
bool in_chain[maxn];
pair<int,int> dp[maxn][2];
int pre[maxn];
stack<int> stk;
template <typename T, typename U>
pair<T,U> operator +(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first + y.first, x.second + y.second);
}
template <typename T, typename U>
pair<T,U> operator -(const pair<T,U> &x, const pair<T,U> &y){
    return make_pair(x.first - y.first, x.second - y.second);
}

void dfs_dp(int u){
    dp[u][0] = make_pair(0,0);
    for (int v : E[u]){
        if (in_chain[v])continue;
        dfs_dp(v);
        dp[u][0] =dp[u][0] + max(dp[v][0], dp[v][1]);
    }
    dp[u][1] = dp[u][0];
    pair<int,int> max_del = make_pair(-inf,-inf);
    for (int v : E[u]){
        if (in_chain[v])continue;
        auto del = dp[v][0] - max(dp[v][0],dp[v][1]);
        del.first ++;
        if (color[v] ^ color[u])del.second ++;
        if (del > max_del){
            max_del = del;
            pre[u] = v;
        }
    }
    dp[u][1] = dp[u][1] +  max_del;
}
void dfs_ans(int u,int flag,vector<pair<int,int> > & dp_ans){
//    cout<<"dfs_ans:"<<u<<","<<flag<<endl;
    for (int v : E[u]){
        if (in_chain[v])continue;
        if (flag == 1 && v == pre[u]){
            dp_ans.push_back(make_pair(u,pre[u]));
            dfs_ans(v,0,dp_ans);
        }else{
            if (dp[v][1] > dp[v][0]){
                dfs_ans(v,1,dp_ans);
            }else{
                dfs_ans(v,0,dp_ans);
            }
        }
    }
}
void dfs(int u){
    vis[u] = 1;
    stk.push(u);
    for (int v: E[u]){
        if (!vis[v])dfs(v);
        else if(vis[v] == 2)continue;
        else if (vis[v] ==1){
            while (1){
                chain.push_back(stk.top());
                vis[stk.top()] = 2;
                stk.pop();
                if (chain.back() == v)break;
            }
            while (!stk.empty()){
                vis[stk.top()] = 2;
                stk.pop();
            }
            reverse(chain.begin(),chain.end());
            for (int x : chain)in_chain[x] = 1;
            throw 1;
        }else assert(0);
    }
    vis[u] = 2;
    stk.pop();
}
vector<pair<int,int> > ans(0);
pair<int,int> DP[maxn][2];
bool used[maxn];
pair<pair<int,int>,vector<pair<int,int> > > dp_ans(){
/*        cout<<"chain: ";

    for (int x : chain){
        cout<<x<<" ";
    }
    cout<<endl;
*/

    int pre = 0;
    for (int x : chain){
        //edge not used
        for (int i=0;i<2;i++){
            DP[x][i] = max(DP[pre][0],DP[pre][1]) + dp[x][i];
        }
        //edge used
        if (pre){
            pair<int,int> temp_dp = DP[pre][0] + dp[x][0] + make_pair((int)(pre != 0),(int)(pre != 0 && color[x] != color[pre]));
            if (temp_dp >DP[x][1]){
                DP[x][1] = temp_dp;
                used[x] = 1;
            }
        }
        pre = x;
    }
    vector<pair<int,int> > mans(0);
    vector<int> chain = ::chain;
    int now,flag;
    int edp = chain.back();
    now = chain.back();
    if (DP[now][1] > DP[now][0])flag = 1;
    else flag = 0;
    chain.pop_back();
    if (flag == 1 && used[now])dfs_ans(now,0,mans);
    else dfs_ans(now,flag,mans);
    while (!chain.empty()){
        int tail = chain.back();
        chain.pop_back();
        if (flag == 1 && used[now]){
            mans.push_back(make_pair(tail,now));
            now = tail;
            flag = 0;
        }else{
            if (DP[tail][1] > DP[tail][0])flag = 1;
            else flag = 0;
            now = tail;
        }
        if (flag == 1 && used[now])dfs_ans(now,0,mans);
        else dfs_ans(now,flag,mans);
    }
    return make_pair(max(DP[edp][0],DP[edp][1]),mans);
}
pair<int,int> dp_out;
void work(){

    for (int root : chain){
        dfs_dp(root);
  //      cout<<"dp["<<root<<"][0] = "<<dp[root][0].first<<","<<dp[root][0].second
  //          <<"   dp["<<root<<"][1] = "<<dp[root][1].first<<","<<dp[root][1].second<<endl;
    }

    pair<int,int> dp1;
    vector<pair<int,int> > ans1;
    tie(dp1,ans1)= dp_ans();
  //  cout<<dp1.first<<" "<<dp1.second<<endl;
    int head = chain.front();
    reverse(chain.begin(),chain.end());
    chain.pop_back();
    reverse(chain.begin(),chain.end());
    chain.push_back(head);
    pair<int,int> dp2;
    vector<pair<int,int> > ans2;
    tie(dp2,ans2) = dp_ans();
   // cout<<dp2.first<<" "<<dp2.second<<endl;
    if (dp1 > dp2){
        dp_out = dp_out + dp1;
        ans.insert(ans.end(),ans1.begin(),ans1.end());
    }else{
        dp_out = dp_out + dp2;
        ans.insert(ans.end(),ans2.begin(),ans2.end());
    }
}
void search_chain(){
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            while (!stk.empty())stk.pop();
            try{
                dfs(i);
            }catch(int x){
                work();
                chain.clear();
            }
        }
    }
}

int main(){
    cin>>n;
    for (int i=1;i<=n;i++){
        int v;
        cin>>v>>color[i];
        E[v].push_back(i);
    }
    search_chain();
    cout<<dp_out.first<<" "<<dp_out.second<<endl;
    for (auto pr : ans){
        int u,v;
        tie(u,v) = pr;
        cout<<v<<" "<<u<<endl;
    }
    return 0;
}


#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n;
int s[100010];
vector<int>G[100010];
int p[100010];
int mark;
bool f[100010];
bool vis[100010];
bool col[100010];
pair<int,int>dp[100010][2];
vector<int>v;
vector<int>ng[100010];
vector<pair<int,int> >prework;
vector<pair<int,int> >all;
int T,E;
int st[100010][2];
int nxt[100010];
void dfs(int x)
{
	v.push_back(x);
	vis[x]=col[x]=true;
	for(int i=0;i<G[x].size();i++)
	{
		int y=G[x][i];
		if(col[y])mark=y;
		if(vis[y])continue;
		dfs(y);
	}
	col[x]=false;
}
void gtans(int x,int p)
{
	dp[x][0]=dp[x][1]=make_pair(0,0);
	pair<int,int>res=make_pair(0,0);
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		gtans(y,x);
		if(dp[y][1]>dp[y][0])res.first+=dp[y][1].first,res.second+=dp[y][1].second,st[y][0]=st[y][1]=1;
		else res.first+=dp[y][0].first,res.second+=dp[y][0].second,st[y][0]=st[y][1]=0;
	}
	dp[x][0]=res;
	int pos=-1;
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		pair<int,int>tmp=res;
		tmp.first-=(dp[y][st[y][1]].first-dp[y][0].first);
		tmp.second-=(dp[y][st[y][1]].second-dp[y][0].second);
		tmp.first++;
		tmp.second+=(s[x]!=s[y]);
		if(tmp>dp[x][1])
		{
			dp[x][1]=tmp;
			pos=y;
		}
	}
	if(pos!=-1)
	{
		nxt[x]=pos;
		st[pos][1]=0;
	}
}
void gtedge(int x,int p,int state)
{
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		if(!state)
		{
			gtedge(y,x,st[y][0]);
		}
		else
		{
			if(y==nxt[x])prework.push_back(make_pair(x,y));
			gtedge(y,x,st[y][1]);
		}
	}
}
pair<int,int> solve()
{
	for(int i=0;i<v.size();i++)ng[v[i]].clear();
	for(int i=0;i<v.size();i++)
	{
		if(v[i]==mark)continue;
		ng[p[v[i]]].push_back(v[i]);
		ng[v[i]].push_back(p[v[i]]);
	}
	gtans(mark,-1);
	prework.clear();
	if(dp[mark][0]>dp[mark][1])
	{
		gtedge(mark,-1,0);
		return dp[mark][0];
	}
	else
	{
		gtedge(mark,-1,1);
		return dp[mark][1];
	}
}
void partsolve(int i)
{
	mark=0;
	v.clear();
	dfs(i);
	pair<int,int>res=solve();
	vector<pair<int,int> >tmp=prework;
	mark=p[mark];
	pair<int,int>nres=solve();
	if(nres<res)
	{
		T+=res.first;
		E+=res.second;
		for(int i=0;i<tmp.size();i++)all.push_back(tmp[i]);
	}
	else
	{
		T+=nres.first;
		E+=nres.second;
		for(int i=0;i<prework.size();i++)all.push_back(prework[i]);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",p+i,s+i);
		G[p[i]].push_back(i);
		G[i].push_back(p[i]);
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i])continue;
		partsolve(i);
	}
	printf("%d %d\n",T,E);
	for(int i=0;i<all.size();i++)
	{
		printf("%d %d\n",all[i].first,all[i].second);
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n;
int s[100010];
vector<pair<int,int> >G[100010];
int p[100010];
int mark;
bool f[100010];
bool vis[100010];
bool col[100010];
pair<int,int>dp[100010][2];
vector<int>v;
vector<int>ng[100010];
vector<pair<int,int> >prework;
vector<pair<int,int> >all;
int T,E;
int st[100010][2];
int nxt[100010];
bool used[100010];
void dfs(int x)
{
	v.push_back(x);
	vis[x]=col[x]=true;
	for(int i=0;i<G[x].size();i++)
	{
		int y=G[x][i].first;
		if(used[G[x][i].second])continue;
		used[G[x][i].second]=true;
		if(col[y])mark=y;
		if(vis[y])continue;
		dfs(y);
	}
	col[x]=false;
}
void gtans(int x,int p)
{
	dp[x][0]=dp[x][1]=make_pair(0,0);
	pair<int,int>res=make_pair(0,0);
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		gtans(y,x);
		if(dp[y][1]>dp[y][0])res.first+=dp[y][1].first,res.second+=dp[y][1].second,st[y][0]=st[y][1]=1;
		else res.first+=dp[y][0].first,res.second+=dp[y][0].second,st[y][0]=st[y][1]=0;
	}
	dp[x][0]=res;
	int pos=-1;
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		pair<int,int>tmp=res;
		tmp.first-=(dp[y][st[y][1]].first-dp[y][0].first);
		tmp.second-=(dp[y][st[y][1]].second-dp[y][0].second);
		tmp.first++;
		tmp.second+=(s[x]!=s[y]);
		if(tmp>dp[x][1])
		{
			dp[x][1]=tmp;
			pos=y;
		}
	}
	if(pos!=-1)
	{
		nxt[x]=pos;
		st[pos][1]=0;
	}
}
void gtedge(int x,int p,int state)
{
	for(int i=0;i<ng[x].size();i++)
	{
		int y=ng[x][i];
		if(y==p)continue;
		if(!state)
		{
			gtedge(y,x,st[y][0]);
		}
		else
		{
			if(y==nxt[x])prework.push_back(make_pair(x,y));
			gtedge(y,x,st[y][1]);
		}
	}
}
pair<int,int> solve()
{
	for(int i=0;i<v.size();i++)ng[v[i]].clear();
	for(int i=0;i<v.size();i++)
	{
		if(v[i]==mark)continue;
		ng[p[v[i]]].push_back(v[i]);
		ng[v[i]].push_back(p[v[i]]);
	}
	gtans(mark,-1);
	prework.clear();
	if(dp[mark][0]>dp[mark][1])
	{
		gtedge(mark,-1,0);
		return dp[mark][0];
	}
	else
	{
		gtedge(mark,-1,1);
		return dp[mark][1];
	}
}
void partsolve(int i)
{
	mark=0;
	v.clear();
	dfs(i);
	pair<int,int>res=solve();
	vector<pair<int,int> >tmp=prework;
	mark=p[mark];
	pair<int,int>nres=solve();
	if(nres<res)
	{
		T+=res.first;
		E+=res.second;
		for(int i=0;i<tmp.size();i++)all.push_back(tmp[i]);
	}
	else
	{
		T+=nres.first;
		E+=nres.second;
		for(int i=0;i<prework.size();i++)all.push_back(prework[i]);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",p+i,s+i);
		G[p[i]].push_back(make_pair(i,i));
		G[i].push_back(make_pair(p[i],i));
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i])continue;
		partsolve(i);
	}
	printf("%d %d\n",T,E);
	for(int i=0;i<all.size();i++)
	{
		printf("%d %d\n",all[i].first,all[i].second);
	}
	return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define pii pair <int, int>

int n, x, y, z, type[100100];
set < int > graph[100100];
bool visited[100100], cycle;

void dfs(int u, int p) {
	visited[u] = true;
	for (auto v : graph[u]) {
		if (!visited[v])
			dfs(v, u);
		else if (v != p && !cycle) {
			x = v; y = u; z = p;
			cycle = true;
		}
	}
}

#define state vector < int >
state dp[5][100100][2], ans[5];

state solve(int u, int t, int p, int ind) {
	if (dp[ind][u][t] != state({-1, -1, -1}))
		return dp[ind][u][t];

	int total = 0, bg = 0, next = 0;

	for (auto v : graph[u])
		if (v != p) {
			state aux = solve(v, 0, u, ind);
			total += aux[0]; bg += aux[1];
		}

	if (t == 0) {
		int a = total, b = bg, id = 0;
		for (auto v : graph[u])
			if (v != p) {
				pii aux = pii(total, bg);
				aux.first++;
				if (type[v] != type[u])
					aux.second++;
				state sub = solve(v, 0, u, ind);
				state sum = solve(v, 1, u, ind);
				aux.first = aux.first - sub[0] + sum[0];
				aux.second = aux.second - sub[1] + sum[1];

				if (aux > pii(a, b)) {
					a = aux.first;
					b = aux.second;
					id = v;
				}
			}

		total = a; bg = b, next = id;
	}

	return dp[ind][u][t] = state({total, bg, next});
}

vector < pii > selected;

void find_edges(int ind, int u, int t, int p, pii removed = pii(0, 0)) {
	if (t == 0 && dp[ind][u][t][2] != 0)
		selected.push_back(pii(u, dp[ind][u][t][2]));

	for (auto v : graph[u])
		if (v != p && (min(u, v) != removed.first || max(u, v) != removed.second)) {
			if (t == 1) find_edges(ind, v, 0, u, removed);
			else find_edges(ind, v, v == dp[ind][u][t][2], u, removed);
		}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d", &x, &type[i]);
		graph[i].insert(x);
		graph[x].insert(i);
	}

	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 100100; j++)
			dp[i][j][0] = dp[i][j][1] = state({-1, -1, -1});

	int total = 0, bg = 0;

	for (int i = 1; i <= n; i++)
		if (!visited[i]) {
			cycle = false;
			dfs(i, 0);
			if (cycle) {
				graph[x].erase(y); graph[y].erase(x);
				ans[0] = solve(i, 0, 0, 0);
				ans[1] = solve(i, 1, 0, 1);
				graph[x].insert(y); graph[y].insert(x);

				graph[y].erase(z); graph[z].erase(y);
				ans[2] = solve(i, 0, 0, 2);
				ans[3] = solve(i, 1, 0, 3);
				graph[y].insert(z); graph[z].insert(y);

				int best = 0;
				for (int i = 1; i < 4; i++) {
					if (ans[best][0] < ans[i][0]) best = i;
					else if (ans[best][0] == ans[i][0] && ans[best][1] < ans[i][1]) best = i;
				}

				total += ans[best][0];
				bg += ans[best][1];
				find_edges(best, i, best % 2, 0, best < 2? pii(min(x, y), max(x, y)) : pii(min(y, z), max(y, z)));
			}
			else {
				ans[0] = solve(i, 0, 0, 0);
				ans[1] = solve(i, 1, 0, 1);
				int best = 0;
				if (ans[0][0] < ans[1][0]) best = 1;
				else if (ans[0][0] == ans[1][0] && ans[0][1] < ans[1][1]) best = 1;

				total += ans[best][0];
				bg += ans[best][1];
				find_edges(best, i, best % 2, 0);
			}
		}

	printf("%d %d\n", total, bg);
	for (int i = 0; i < (int)selected.size(); i++)
		printf("%d %d\n", selected[i].first, selected[i].second);

	return 0;
}

/*
5
2 1
3 2
2 2
2 1
4 1
*/


#include <bits/stdc++.h>

using namespace std;

#define pii pair <int, int>

int n, x, y, z, type[100100];
set < int > graph[100100];
bool visited[100100], cycle;

void dfs(int u, int p) {
	visited[u] = true;
	for (auto v : graph[u]) {
		if (!visited[v])
			dfs(v, u);
		else if (v != p && !cycle) {
			x = v; y = u; z = p;
			cycle = true;
		}
	}
}

#define state vector < int >
state dp[5][100100][2], ans[5];

state solve(int u, int t, int p, int ind) {
	if (dp[ind][u][t] != state({-1, -1, -1}))
		return dp[ind][u][t];

	int total = 0, bg = 0, next = 0;

	for (auto v : graph[u])
		if (v != p) {
			state aux = solve(v, 0, u, ind);
			total += aux[0]; bg += aux[1];
		}

	if (t == 0) {
		int a = 0, b = 0, id = 0;
		for (auto v : graph[u])
			if (v != p) {
				pii aux = pii(total, bg);
				aux.first++;
				if (type[v] != type[u])
					aux.second++;
				state sub = solve(v, 0, u, ind);
				state sum = solve(v, 1, u, ind);
				aux.first = aux.first - sub[0] + sum[0];
				aux.second = aux.second - sub[1] + sum[1];

				if (aux > pii(a, b)) {
					a = aux.first;
					b = aux.second;
					id = v;
				}
			}

		total = a; bg = b, next = id;
	}

	return dp[ind][u][t] = state({total, bg, next});
}

vector < pii > selected;

void find_edges(int ind, int u, int t, int p, pii removed = pii(0, 0)) {
	if (t == 0 && dp[ind][u][t][2] != 0)
		selected.push_back(pii(u, dp[ind][u][t][2]));

	for (auto v : graph[u])
		if (v != p && (min(u, v) != removed.first || max(u, v) != removed.second)) {
			if (t == 1) find_edges(ind, v, 0, u, removed);
			else find_edges(ind, v, v == dp[ind][u][t][2], u, removed);
		}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d", &x, &type[i]);
		graph[i].insert(x);
		graph[x].insert(i);
	}

	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 100100; j++)
			dp[i][j][0] = dp[i][j][1] = state({-1, -1, -1});

	int total = 0, bg = 0;

	for (int i = 1; i <= n; i++)
		if (!visited[i]) {
			cycle = false;
			dfs(i, 0);
			if (cycle) {
				graph[x].erase(y); graph[y].erase(x);
				ans[0] = solve(i, 0, 0, 0);
				ans[1] = solve(i, 1, 0, 1);
				graph[x].insert(y); graph[y].insert(x);

				graph[y].erase(z); graph[z].erase(y);
				ans[2] = solve(i, 0, 0, 2);
				ans[3] = solve(i, 1, 0, 3);
				graph[y].insert(z); graph[z].insert(y);

				int best = 0;
				for (int i = 1; i < 4; i++) {
					if (ans[best][0] < ans[i][0]) best = i;
					else if (ans[best][0] == ans[i][0] && ans[best][1] < ans[i][1]) best = i;
				}

				total += ans[best][0];
				bg += ans[best][1];
				find_edges(best, i, best % 2, 0, best < 2? pii(min(x, y), max(x, y)) : pii(min(y, z), max(y, z)));
			}
			else {
				ans[0] = solve(i, 0, 0, 0);
				ans[1] = solve(i, 1, 0, 1);
				int best = 0;
				if (ans[0][0] < ans[1][0]) best = 1;
				else if (ans[0][0] == ans[1][0] && ans[0][1] < ans[1][1]) best = 1;

				total += ans[best][0];
				bg += ans[best][1];
				find_edges(best, i, best % 2, 0);
			}
		}

	printf("%d %d\n", total, bg);
	for (int i = 0; i < (int)selected.size(); i++)
		printf("%d %d\n", selected[i].first, selected[i].second);

	return 0;
}

/*
5
2 1
3 2
2 2
2 1
4 1
*/