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

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

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

const int Maxn=1e5+5;

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

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

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

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

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

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


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

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

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

const int Maxn=1e5+5;

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

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

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

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

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

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


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

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

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

const int Maxn=1e5+5;

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

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

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

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

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

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

			}
			else if(cp[0]>cp[1]){
				ans+=cans[0];
				p+=cp[0];
				for(auto v:cansp[0])ansp.push_back(v);
			}
			else{
				ans+=cans[1];
				p+=cp[1];
				for(auto v:cansp[1])ansp.push_back(v);
			}
			cansp[0].clear();
			cansp[1].clear();
		}
	}
	printf("%d %d\n",ans,p);
	for(auto v:ansp){
		printf("%d %d\n",v.first,v.second);
	}
return 0;
}


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

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

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

const int Maxn=1e5+5;

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

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

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

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

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

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


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

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

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

const int Maxn=1e5+5;

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

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

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

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

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

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


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1) if(ds[i] != ds[p[i]]) use[i] = 1 , ds.Union(i , p[i]) , G[i].pb(p[i]) , G[p[i]].pb(i);
	REP(i , 1 , n + 1) if(use[i] == 0){
		int a = i , b = p[i];
		dot = a , DFS0(a , a) , T[a] = b;
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i];
	solveinit();
	// REP(i , 1 , n + 1) cout << p[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << T[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << ds[i] << " "; cout << endl;
	REP(i , 1 , n + 1) assert(T[i] != i && T[i] != 0);
	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1) if(ds[i] != ds[p[i]]) use[i] = 1 , ds.Union(i , p[i]);
	REP(i , 1 , n + 1) if(use[i] == 0){
		int a = i , b = p[i];
		dot = a , DFS0(a , a) , T[a] = b;
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i] , G[i].pb(p[i]) , G[p[i]].pb(i);
	solveinit();
	// REP(i , 1 , n + 1) cout << p[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << T[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << ds[i] << " "; cout << endl;
	REP(i , 1 , n + 1) assert(T[i] != i && T[i] != 0);
	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1){
		int a = i , b = p[i];
		if(ds[a] != ds[b]) ds.Union(a , b);
		else dot = a , DFS0(a , a) , T[a] = b;
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i] , G[i].pb(p[i]) , G[p[i]].pb(i);
	solveinit();
	// REP(i , 1 , n + 1) cout << p[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << T[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << ds[i] << " "; cout << endl;

	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);
	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
5
5 2
3 2
5 1
2 1
4 2
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1){
		int a = i , b = p[i];
		if(ds[a] != ds[b]) ds.Union(a , b);
		else dot = a , DFS0(a , b);
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i] , G[i].pb(p[i]) , G[p[i]].pb(i);
	solveinit();
	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	if(use[now] == 0) use[now] = 1;
	else return ;
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1){
		int a = i , b = p[i];
		if(ds[a] != ds[b]) ds.Union(a , b);
		else dot = a , DFS0(a , a) , T[a] = b;
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i] , G[i].pb(p[i]) , G[p[i]].pb(i);
	solveinit();
	// REP(i , 1 , n + 1) cout << p[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << T[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << ds[i] << " "; cout << endl;
	REP(i , 1 , n + 1) assert(T[i] != i && T[i] != 0);
	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);
	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


/*input
8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100090
#define INF 0x3f3f3f3f

PII operator + (PII x , PII y){ return mp(x.A + y.A , x.B + y.B); }
PII operator - (PII x , PII y){ return mp(x.A - y.A , x.B - y.B); }

int n , dot , p[MAX] , x[MAX] , u[MAX];
PII dp[MAX][2] , totalans = mp(0 , 0);

vector<PII> ans , tmp;
vector<int> v[MAX];

void DFS(int now){
	u[now] = 1 , dp[now][0] = dp[now][1] = mp(0 , 0);
	for(auto to : v[now]) if(to != dot) DFS(to);
	for(auto to : v[now]) if(to != dot)
		dp[now][0] = max(dp[to][0] , dp[to][1]) + dp[now][0];
	for(auto to : v[now]) if(to != dot)
		dp[now][1] = max(dp[now][1] , dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]));
}
void getans(int now , int ty){
	if(ty == 0){
		for(auto to : v[now]) if(to != dot){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
	if(ty == 1){
		int idx = -1;
		for(auto to : v[now]) if(to != dot){
			if(dp[now][0] - max(dp[to][0] , dp[to][1]) + dp[to][0] + mp(1 , x[now] != x[to]) == dp[now][1]) idx = to;
		}
		assert(idx != -1);
		tmp.pb(now , idx);
		getans(idx , 0);
		for(auto to : v[now]) if(to != dot && to != idx){
			if(dp[to][0] >= dp[to][1])
				 getans(to , 0);
			else getans(to , 1);
		}
	}
}
void solve(int now){
	while(u[now] == 0) u[now] = 1 , now = p[now];
	PII val = mp(0 , 0);
	REP(times , 0 , 2){
		dot = now , DFS(now);
		if(max(dp[now][0] , dp[now][1]) > val){
			val = max(dp[now][0] , dp[now][1]);
			tmp.clear();
			if(dp[now][0] >= dp[now][1])
				 getans(now , 0);
			else getans(now , 1);
		}
		now = p[now];
	}
	totalans = totalans + val;
	for(auto to : tmp) ans.pb(to);
}
struct djs{
	int x[MAX];
	void init(){ REP(i , 0 , MAX) x[i] = i; }
	int Find(int a){ return a == x[a] ? a : x[a] = Find(x[a]); }
	void Union(int a , int b){ x[Find(a)] = Find(b); }
	int operator[](int a){ return Find(a); }
} ds;
vector<int> G[MAX];
int use[MAX] , T[MAX];
void DFS0(int now , int fa){
	T[now] = fa;
	for(auto to : G[now]) if(to != fa && to != dot){
		DFS0(to , now);
	}
}
void solveinit(){
	ds.init();
	REP(i , 1 , n + 1){
		int a = i , b = p[i];
		if(ds[a] != ds[b]) ds.Union(a , b);
		else dot = a , DFS0(a , a) , T[a] = b;
	}
}
int32_t main(){
	IOS;
	cin >> n;
	REP(i , 1 , n + 1) cin >> p[i] >> x[i] , G[i].pb(p[i]) , G[p[i]].pb(i);
	solveinit();
	// REP(i , 1 , n + 1) cout << p[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << T[i] << " " ; cout << endl;
	// REP(i , 1 , n + 1) cout << ds[i] << " "; cout << endl;
	REP(i , 1 , n + 1) assert(T[i] != i && T[i] != 0);
	REP(i , 1 , n + 1) p[i] = T[i];
	REP(i , 1 , n + 1) v[p[i]].pb(i);

	REP(i , 1 , n + 1) if(u[i] == 0) solve(i);
	cout << totalans.A << " " << totalans.B << endl;
	for(auto to : ans) cout << to.A << " " << to.B << endl;
    return 0;
}


#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int MAXN = 100100;
const int INF = 1000000007;

int f[MAXN], s[MAXN], tag[MAXN], pre[MAXN];
vector<int> c[MAXN];
PII dp[MAXN][2];

PII operator+(const PII& lhs, const PII& rhs) {
	return make_pair(lhs.first + rhs.first, lhs.second + rhs.second);
}

PII operator-(const PII& lhs, const PII& rhs) {
	return make_pair(lhs.first - rhs.first, lhs.second - rhs.second);
}

void gao(int v) {
	if (tag[v] == 0) {
		tag[v] = 1;
	}
	dp[v][0] = make_pair(0, 0);
	dp[v][1] = make_pair(0, 0);
	pre[v] = -1;
	for (vector<int>::const_iterator w = c[v].begin(); w != c[v].end(); ++w) {
		if (tag[*w] == -1) {
			continue;
		}
		gao(*w);
		dp[v][0] = dp[v][0] + dp[*w][1];
		PII tmp = make_pair(1, s[v] == s[*w] ? 0 : 1) - (dp[*w][1] - dp[*w][0]);
		if (dp[v][1] < tmp) {
			dp[v][1] = tmp;
			pre[v] = *w;
		}
	}
	dp[v][1] = dp[v][1] + dp[v][0];
//	printf("@%d: <%d, %d> <%d, %d>\n", v, dp[v][0].first, dp[v][0].second, dp[v][1].first, dp[v][1].second);
}

void dump(int v, bool f, vector<PII>& ret) {
	for (vector<int>::const_iterator w = c[v].begin(); w != c[v].end(); ++w) {
		if (tag[*w] == -1) {
			continue;
		}
		if (!f || *w != pre[v]) {
			dump(*w, true, ret);
		} else {
			ret.push_back(make_pair(v, *w));
			dump(*w, false, ret);
		}
	}
}

PII cdp[MAXN][2];
int cpre[MAXN];
PII ans;
vector<PII> todo;

void solve(int v) {
	while (tag[v] == 0) {
		tag[v] = 1;
		v = f[v];
	}
	vector<int> circ(1, v);
	for (int w = f[v]; w != v; w = f[w]) {
		circ.push_back(w);
	}

	int n = (int)circ.size();

//	printf("solve(%d):\n", v);
//	for (int i = 0; i < n; ++i) {
//		printf("\t%d", circ[i]);
//	}
//	puts("");

	for (int i = 0; i < n; ++i) {
		tag[circ[i]] = -1;
	}
	for (int i = 0; i < n; ++i) {
		gao(circ[i]);
//		printf("gao(%d): <%d, %d> <%d, %d>\n", circ[i], dp[circ[i]][0].first, dp[circ[i]][0].second, dp[circ[i]][1].first, dp[circ[i]][1].second);
	}

	// (n-1) -> 0: no
	cdp[circ.front()][0] = dp[circ.front()][0];
	cdp[circ.front()][1] = dp[circ.front()][1];
	cpre[circ.front()] = -1;
	for (vector<int>::const_iterator i = circ.begin() + 1; i != circ.end(); ++i) {
		vector<int>::const_iterator j = i - 1;
		cdp[*i][0] = cdp[*j][1] + dp[*i][0];
		cdp[*i][1] = cdp[*j][1] + dp[*i][1];
		cpre[*i] = -1;
		PII tmp = cdp[*j][0] + make_pair(1, s[*i] == s[*j] ? 0 : 1) + dp[*i][0];
		if (cdp[*i][1] < tmp) {
			cdp[*i][1] = tmp;
			cpre[*i] = 1;
		}
	}
	PII best = cdp[circ.back()][1];
//	printf("b %d %d\n", best.first, best.second);
	vector<PII> how;
	for (vector<int>::const_iterator i = circ.end() - 1; i >= circ.begin(); ) {
		if (cpre[*i] == -1) {
			dump(*i, true, how);
			--i;
		} else {
			dump(*i, false, how);
			dump(*(i - 1), false, how);
			how.push_back(make_pair(*i, *(i - 1)));
			i -= 2;
		}
	}

	rotate(circ.begin(), circ.begin() + 1, circ.end());
	cdp[circ.front()][0] = dp[circ.front()][0];
	cdp[circ.front()][1] = dp[circ.front()][1];
	cpre[circ.front()] = -1;
	for (vector<int>::const_iterator i = circ.begin() + 1; i != circ.end(); ++i) {
		vector<int>::const_iterator j = i - 1;
		cdp[*i][0] = cdp[*j][1] + dp[*i][0];
		cdp[*i][1] = cdp[*j][1] + dp[*i][1];
		cpre[*i] = -1;
		PII tmp = cdp[*j][0] + make_pair(1, s[*i] == s[*j] ? 0 : 1) + dp[*i][0];
		if (cdp[*i][1] < tmp) {
			cdp[*i][1] = tmp;
			cpre[*i] = 1;
		}
	}
//	printf("b %d %d\n", cdp[circ.back()][1].first, cdp[circ.back()][1].second);
	if (best < cdp[circ.back()][1]) {
		best = cdp[circ.back()][1];
		how.clear();
		for (vector<int>::const_iterator i = circ.end() - 1; i >= circ.begin(); ) {
			if (cpre[*i] == -1) {
				dump(*i, true, how);
				--i;
			} else {
				dump(*i, false, how);
				dump(*(i - 1), false, how);
				how.push_back(make_pair(*i, *(i - 1)));
				i -= 2;
			}
		}
	}

	ans = ans + best;
	todo.insert(todo.end(), how.begin(), how.end());
}

int main() {
	int n;

	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &f[i], &s[i]);
		c[f[i]].push_back(i);
	}
	ans = make_pair(0, 0);
	todo.clear();
	for (int i = 1; i <= n; ++i) {
		if (tag[i] == 0) {
			solve(i);
		}
	}
	printf("%d %d\n", ans.first, ans.second);
	for (vector<PII>::const_iterator it = todo.begin(); it != todo.end(); ++it) {
		printf("%d %d\n", it->first, it->second);
	}

	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
struct prs{
	int s1,s2;
	prs operator+(prs u)const
	{
		return (prs){s1+u.s1,s2+u.s2};
	}
	prs operator-(prs u)const
	{
		return (prs){s1-u.s1,s2-u.s2};
	}
	void operator+=(prs u)
	{
		*this=*this+u;
	}
	bool operator<(prs u)const
	{
		if(s1!=u.s1)return s1<u.s1;
		return s2<u.s2;
	}
};
int n;
vector<pair<int,int> >G[100010];
int s[100010];
prs dp[100010][2];
prs ans[100010];
int ans1,ans2;
int to[100010];
int dep[100010];
int up[100010];
int m;
void solve(int x,int p)
{
	dp[x][0]=dp[x][1]=(prs){0,0};
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		solve(y,id);
		dp[x][0]+=max(dp[y][0],dp[y][1]);
	}
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		prs t=(prs){1,s[x]!=s[y]};
		prs val=dp[x][0]+dp[y][0]-max(dp[y][0],dp[y][1])+t;
		if(dp[x][1]<val)dp[x][1]=val,to[x]=y;
	}
}
void getans(int x,int st,int p)
{
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		if(!st || to[x]!=y)
		{
			if(dp[y][1]<dp[y][0])getans(y,0,id);
			else getans(y,1,id);
		}
		else
		{
			ans[++ans1]=(prs){x,y};
			if(s[x]!=s[y])ans2++;
			getans(y,0,id);
		}
	}
}
void dfs(int x,int p)
{
	dep[x]++;
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p)continue;
		if(!dep[y])
		{
			up[y]=id;
			dep[y]=dep[x];
			dfs(y,id);
		}
		else if(dep[y]<dep[x])
		{
			prs r1,r2;
			int s1,s2;
			m=id;
			solve(x,0);
			if(dp[x][1]<dp[x][0])r1=dp[x][0],s1=0;else r1=dp[x][1],s1=1;
			m=up[x];
			solve(x,0);
			if(dp[x][1]<dp[x][0])r2=dp[x][0],s2=0;else r2=dp[x][1],s2=1;
			if(r2<r1)
			{
				m=id;
				getans(x,s1,0);
			}
			else getans(x,s2,0);
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int f;
		scanf("%d%d",&f,s+i);
		G[f].push_back(make_pair(i,i));
		G[i].push_back(make_pair(f,i));
	}
	for(int i=1;i<=n;i++)
	{
		if(dep[i])continue;
		dfs(i,0);
	}
	printf("%d %d\n",ans1,ans2);
	for(int i=1;i<=ans1;i++)printf("%d %d\n",ans[i].s1,ans[i].s2);
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
struct prs{
	int s1,s2;
	prs operator+(prs u)const
	{
		return (prs){s1+u.s1,s2+u.s2};
	}
	prs operator-(prs u)const
	{
		return (prs){s1-u.s1,s2-u.s2};
	}
	void operator+=(prs u)
	{
		*this=*this+u;
	}
	bool operator<(prs u)const
	{
		if(s1!=u.s1)return s1<u.s1;
		return s2<u.s2;
	}
};
int n;
vector<pair<int,int> >G[100010];
int s[100010];
prs dp[100010][2];
prs ans[100010];
int ans1,ans2;
int to[100010];
int dep[100010];
int up[100010];
int m;
void solve(int x,int p)
{
	dp[x][0]=dp[x][1]=(prs){0,0};
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		solve(y,id);
		dp[x][0]+=max(dp[y][0],dp[y][1]);
	}
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		prs t=(prs){1,s[x]!=s[y]};
		prs val=dp[x][0]+dp[y][0]-max(dp[y][0],dp[y][1])+t;
		if(dp[x][1]<val)dp[x][1]=val,to[x]=y;
	}
}
void getans(int x,int st,int p)
{
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p || id==m)continue;
		if(!st || to[x]!=y)
		{
			if(dp[y][1]<dp[y][0])getans(y,0,id);
			else getans(y,1,id);
		}
		else
		{
			ans[++ans1]=(prs){x,y};
			if(s[x]!=s[y])ans2++;
			getans(y,0,id);
		}
	}
}
void dfs(int x,int p)
{
	dep[x]++;
	for(int i=0;i<(int)G[x].size();i++)
	{
		int y=G[x][i].first,id=G[x][i].second;
		if(id==p)continue;
		if(!dep[y])
		{
			up[y]=id;
			dep[y]=dep[x];
			dfs(y,id);
		}
		else if(dep[y]<dep[x])
		{
			prs r1,r2;
			int s1,s2;
			m=id;
			solve(x,0);
			if(dp[x][1]<dp[x][0])r1=dp[x][0],s1=0;else r1=dp[x][1],s1=1;
			m=up[x];
			solve(x,0);
			if(dp[x][1]<dp[x][0])r2=dp[x][0],s2=0;else r2=dp[x][1],s2=1;
			if(r2<r1)
			{
				m=id;
				solve(x,0);
				getans(x,s1,0);
			}
			else getans(x,s2,0);
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int f;
		scanf("%d%d",&f,s+i);
		G[f].push_back(make_pair(i,i));
		G[i].push_back(make_pair(f,i));
	}
	for(int i=1;i<=n;i++)
	{
		if(dep[i])continue;
		dfs(i,0);
	}
	printf("%d %d\n",ans1,ans2);
	for(int i=1;i<=ans1;i++)printf("%d %d\n",ans[i].s1,ans[i].s2);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define pr complex<int>
#define pb push_back
#define rep(i,a,b) for (int i=a;i<b;i++)
const int N=101000;
pr f[N][2],ans;
int g[N],rt,_[N],s[N],v[N],n;
vector<int> c[N];
vector<pr> p,q;
bool operator<(const pr&a,const pr&b){return real(a)<real(b)||(real(a)==real(b)&&imag(a)<imag(b));}
void dfs(int u) {
 f[u][0]=f[u][1]=pr(0,0);g[u]=0;v[u]=1;
 for (int j:c[u]) {
  if (j==rt) continue;
  dfs(j);
  f[u][0]+=f[j][1];
  pr t=pr(1,s[u]^s[j])-f[j][1]+f[j][0];
  if (f[u][1]<t) f[u][1]=t,g[u]=j;
 }
 f[u][1]+=f[u][0];
}
void gans(int u,bool b) {
 for(int j:c[u]) {
  if (j==rt) continue;
  if (!b||g[u]!=j) gans(j,1); else p.pb(pr(u,j)),gans(j,0);
 }
}
void solve(int u) {
 for(;!v[u];u=_[u])v[u]=1;
 pr r(0,0);
 rep(t,0,2) {
  rt=u;
  dfs(u);
  if (r<f[u][1]) r=f[u][1],p.clear(),gans(u,1);
  u=_[u];
 }
 for(pr j:p) q.pb(j);ans+=r;
}
int main() {
 scanf("%d",&n);
 rep(i,1,n+1) scanf("%d%d",_+i,s+i),--s[i],c[_[i]].pb(i);
 rep(i,1,n+1) if (!v[i]) solve(i);
 printf("%d %d\n",real(ans),imag(ans));
 for(pr j:q) printf("%d %d\n",real(j),imag(j));
}


#include <iostream>
#include <cstdio>
#include <cctype>
#include <vector>
#define il inline
#define vd void
#define rg register
#define pii pair<int,int>
#define mp make_pair
#define mn 100005
#define rep(i,x,y) for(rg int i=x;i<=y;++i)
#define drp(i,x,y) for(rg int i=x;i>=y;--i)
using namespace std;
const int Len=2333333;
char buf[Len],*p1=buf,*p2=buf,duf[Len],*q1=duf;
il char gc(); il int rd(); il vd pc(char c); il vd rt(int x); il vd flush();
int n,cnt,r,h[mn],id[mn],f[mn],s[mn],vis[mn],g[mn];
pii ans,dp[mn][2];
vector <pii> V,A;
struct E{int to,nxt;}e[mn];
il pii operator +(pii a,pii b){return mp(a.first+b.first,a.second+b.second);}
il pii operator -(pii a,pii b){return mp(a.first-b.first,a.second-b.second);}
il vd Add(int u,int v){e[++cnt]=(E){v,h[u]},h[u]=cnt;}
vd Dfs(int u){
    vis[u]=1,g[u]=0,dp[u][0]=dp[u][1]=mp(0,0);
    for(rg int i=h[u];i;i=e[i].nxt) if(i!=r){int v=e[i].to;
        Dfs(v),dp[u][1]=dp[u][1]+dp[v][0];
        pii t=dp[v][1]-dp[v][0]+mp(1,s[u]^s[v]);
        if(dp[u][0]<t) dp[u][0]=t,g[u]=v;
    }
    dp[u][0]=dp[u][0]+dp[u][1];
}
vd Get(int u,int o){
    for(rg int i=h[u];i;i=e[i].nxt) if(i!=r){int v=e[i].to;
        if(!o&&g[u]==v) V.push_back(mp(u,v)),Get(v,1);
        else Get(v,0);
    }
}
il pii Work(int u){
    while(!vis[u]) vis[u]=1,u=f[u];
    r=id[u],Dfs(u);
    pair<int,int> ans=dp[u][0];
    V.clear(),Get(u,0),u=f[u],r=id[u],Dfs(u);
    return ans<dp[u][0]?V.clear(),Get(u,0),dp[u][0]:ans;
}
int main(){
    n=rd();
    rep(i,1,n) f[i]=rd(),Add(f[i],i),id[i]=cnt,s[i]=rd()-1;
    rep(i,1,n) if(!vis[i]) ans=ans+Work(i),A.insert(A.end(),V.begin(),V.end());
    rt(ans.first),pc(' '),rt(ans.second),pc('\n');
    for(rg int i=0;i<(int)A.size();++i) rt(A[i].first),pc(' '),rt(A[i].second),pc('\n');
    return flush(),0;
}

il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Len,stdin),p1==p2)?-1:*p1++;}
il int rd(){char c;
    while(!isdigit(c=gc())&&c!='-');
    int f=c=='-'?c=gc(),1:0,x=c^48;
    while(isdigit(c=gc())) x=((x+(x<<2))<<1)+(c^48);
    return f?-x:x;
}
il vd pc(char c){q1==duf+Len&&fwrite(q1=duf,1,Len,stdout),*q1++=c;}
il vd rt(int x){x<0?pc('-'),x=-x:0,pc((x>=10?rt(x/10),x%10:x)+48);}
il vd flush(){fwrite(duf,1,q1-duf,stdout);}


#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
struct edge
{
	int to,pre;
	bool ban;
	edge(int _t):to(_t),pre(),ban(){}
	bool operator<(edge rhs)const
	{
		return to<rhs.to;
	}
	bool operator==(edge rhs)const
	{
		return to==rhs.to;
	}
};
vector<edge> mp[100001];
pair<int,int> dp[100001][2];
int stat[100001],v1,v2,v3,v4,sex[100001],pre[100001];
void dfs1(int k,int p)
{
	stat[k]=1;
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&stat[u]==1)
		{
			v1=k;
			v2=u;
			v3=k;
			v4=p;
		}
		else if (u!=p&&!stat[u])
			dfs1(u,k);
	}
	stat[k]=2;
}
void dfs2(int k,int p)
{
	dp[k][0]=dp[k][1]=pair<int,int>();
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban)
		{
			dfs2(u,k);
			pair<int,int> mx=max(dp[u][0],dp[u][1]);
			dp[k][0].first+=mx.first;
			dp[k][0].second+=mx.second;
			mp[k][i].pre=(dp[u][0]>=dp[u][1]?0:1);
		}
	}
	int id;
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban)
		{
			pair<int,int> res(dp[k][0].first-dp[u][mp[k][i].pre].first+dp[u][0].first,
							dp[k][0].second-dp[u][mp[k][i].pre].second+dp[u][0].second);
			++res.first;
			if (sex[u]!=sex[k])
				++res.second;
			if (res>dp[k][1])
			{
				dp[k][1]=res;
				id=u;
			}
		}
	}
	pre[k]=id;
}
void generate(int k,int p,bool v,vector<pair<int,int> >& out)
{
	if (v)
	{
		out.push_back(make_pair(k,pre[k]));
		generate(pre[k],k,0,out);
	}
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban&&(v==0||u!=pre[k]))
			generate(u,k,mp[k][i].pre,out);
	}
}
void mark_status(int u,int v,bool ban)
{
	for (size_t i=0;i<mp[u].size();++i)
		if (mp[u][i].to==v)
			mp[u][i].ban=ban;
	for (size_t i=0;i<mp[v].size();++i)
		if (mp[v][i].to==u)
			mp[v][i].ban=ban;
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
	{
		int s;
		scanf("%d%d",&s,sex+i);
		mp[i].push_back(edge(s));
		mp[s].push_back(edge(i));
	}
	for (int i=1;i<=n;++i)
	{
		sort(mp[i].begin(),mp[i].end());
		mp[i].erase(unique(mp[i].begin(),mp[i].end()),mp[i].end());
	}
	pair<int,int> ans1;
	vector<pair<int,int> > ans2;
	for (int i=1;i<=n;++i)
		if (!stat[i])
		{
			dfs1(i,0);
			mark_status(v1,v2,true);
			dfs2(i,0);
			pair<int,int> ansv=max(dp[i][0],dp[i][1]);
			vector<pair<int,int> > ansp;
			generate(i,0,dp[i][0]>=dp[i][1]?0:1,ansp);
			mark_status(v1,v2,false);
			mark_status(v3,v4,true);
			dfs2(i,0);
			pair<int,int> ansv2=max(dp[i][0],dp[i][1]);
			if (ansv2>ansv)
			{
				ansv=ansv2;
				ansp.clear();
				generate(i,0,dp[i][0]>=dp[i][1]?0:1,ansp);
			}
			ans1.first+=ansv.first;
			ans1.second+=ansv.second;
			ans2.insert(ans2.end(),ansp.begin(),ansp.end());
			mark_status(v3,v4,false);
		}
	printf("%d %d\n",ans1.first,ans1.second);
	for (size_t i=0;i<ans2.size();++i)
		printf("%d %d\n",ans2[i].first,ans2[i].second);
	return 0;
}


#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
struct edge
{
	int to,pre;
	bool ban;
	edge(int _t):to(_t),pre(),ban(){}
};
vector<edge> mp[100001];
pair<int,int> dp[100001][2];
int stat[100001],v1,v2,v3,v4,sex[100001],pre[100001];
void dfs1(int k,int p)
{
	stat[k]=1;
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&stat[u]==1)
		{
			v1=k;
			v2=u;
			v3=k;
			v4=p;
		}
		else if (u!=p&&!stat[u])
			dfs1(u,k);
	}
	stat[k]=2;
}
void dfs2(int k,int p)
{
	dp[k][0]=dp[k][1]=pair<int,int>();
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban)
		{
			dfs2(u,k);
			pair<int,int> mx=max(dp[u][0],dp[u][1]);
			dp[k][0].first+=mx.first;
			dp[k][0].second+=mx.second;
			mp[k][i].pre=(dp[u][0]>=dp[u][1]?0:1);
		}
	}
	int id;
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban)
		{
			pair<int,int> res(dp[k][0].first-dp[u][mp[k][i].pre].first+dp[u][0].first,
							dp[k][0].second-dp[u][mp[k][i].pre].second+dp[u][0].second);
			++res.first;
			if (sex[u]!=sex[k])
				++res.second;
			if (res>dp[k][1])
			{
				dp[k][1]=res;
				id=u;
			}
		}
	}
	pre[k]=id;
}
void generate(int k,int p,bool v,vector<pair<int,int> >& out)
{
	if (v)
	{
		out.push_back(make_pair(k,pre[k]));
		generate(pre[k],k,0,out);
	}
	for (size_t i=0;i<mp[k].size();++i)
	{
		int u=mp[k][i].to;
		if (u!=p&&!mp[k][i].ban&&(v==0||u!=pre[k]))
			generate(u,k,mp[k][i].pre,out);
	}
}
void mark_status(int u,int v,bool ban)
{
	for (size_t i=0;i<mp[u].size();++i)
		if (mp[u][i].to==v)
			mp[u][i].ban=ban;
	for (size_t i=0;i<mp[v].size();++i)
		if (mp[v][i].to==u)
			mp[v][i].ban=ban;
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
	{
		int s;
		scanf("%d%d",&s,sex+i);
		mp[i].push_back(edge(s));
		mp[s].push_back(edge(i));
	}
	pair<int,int> ans1;
	vector<pair<int,int> > ans2;
	for (int i=1;i<=n;++i)
		if (!stat[i])
		{
			dfs1(i,0);
			mark_status(v1,v2,true);
			dfs2(i,0);
			pair<int,int> ansv=max(dp[i][0],dp[i][1]);
			vector<pair<int,int> > ansp;
			generate(i,0,dp[i][0]>=dp[i][1]?0:1,ansp);
			mark_status(v1,v2,false);
			mark_status(v3,v4,true);
			dfs2(i,0);
			pair<int,int> ansv2=max(dp[i][0],dp[i][1]);
			if (ansv2>ansv)
			{
				ansp.clear();
				generate(i,0,dp[i][0]>=dp[i][1]?0:1,ansp);
			}
			ans1.first+=ansv.first;
			ans1.second+=ansv.second;
			ans2.insert(ans2.end(),ansp.begin(),ansp.end());
			mark_status(v3,v4,false);
		}
	printf("%d %d\n",ans1.first,ans1.second);
	for (size_t i=0;i<ans2.size();++i)
		printf("%d %d\n",ans2[i].first,ans2[i].second);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string.h>
#include <limits.h>
#include <cfloat>
#include <stack>
#include <queue>
#include <list>
#include <set>
#include <map>
#include <bitset>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define mem(flag) memset(flag,0,sizeof(flag))
#define Mem(flag) memset(flag,-1,sizeof(flag))
#define read(a) a=getInt()
#define En printf("\n")
#define P(a) printf("%d ",a)
#define Pn(a) printf("%d\n",a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define rep(flag, a, b) for (int flag = (a); flag <= (b); ++flag)
#define Rep(flag, a, b) for (int flag = (a); flag >= (b); --flag)

const int MAX = 1e5 + 20;
vector<int> adj[MAX], adj2[MAX];
struct node {
    int parent;
    int ranks;
};
node trees[MAX];
int n,i,j,k;
void ini() {
    for(i=1;i<=n;i++)
        trees[i].parent=i,trees[i].ranks=0;
}
int findSet(int x) {
    if(trees[x].parent!=x) return trees[x].parent=findSet(trees[x].parent);
    else return x;
}
void unions(int x,int y) {
    int rootX=findSet(x),rootY=findSet(y);
    if(rootX==rootY) return;
    if(trees[rootX].ranks>trees[rootY].ranks) swap(rootX,rootY);
    trees[rootX].parent=rootY;
    if(trees[rootX].ranks==trees[rootY].ranks) trees[rootY].ranks++;
}

pair<int, int> noEd[MAX]; int nonum;
struct nn {
    pair<int, int> val; int sel;
};
nn an1[MAX];
nn an2[MAX];
struct nn2 {
    vector<int> invl;
    pair<int, int> val;
};
nn2 ty2[MAX];
int rr[MAX], rr2[MAX], cn, cn2;
vector<int> child1[MAX], child2[MAX];
int f[MAX], s[MAX];
bool vist[MAX];
int dep[MAX];
int cc;

void dfs1(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj[v].size(); i++) {
        int u = adj[v][i];
        if (!vist[u]) {
            child1[v].push_back(u);
            unions(u, v);
            dep[u] = dep[v] + 1;
            dfs1(u);
        } else if (dep[v] < dep[u]) noEd[cc] = {v, u};
    }
}
void dfs2(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj2[v].size(); i++) {
        int u = adj2[v][i];
        if (!vist[u]) {
            child2[v].push_back(u);
            dfs2(u);
        }
    }
}

inline pair<int, int> operator + (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first + b.first, a.second + b.second};
}
inline pair<int, int> operator - (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first - b.first, a.second - b.second};
}
inline bool operator > (const pair<int, int> &a, const pair<int, int> &b) {
    if (a.first != b.first) return a.first > b.first;
    else return a.second > b.second;
}

pair<int, int> sol1(int v, int ty) {
    vector<int> *child; nn *an;
    if (ty == 1) child = child1, an = an1;
    else child = child2, an = an2;
    if (an[v].val != (pair<int, int>) {-1, -1}) return an[v].val;
    pair<int, int> t1 = {0, 0};
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        t1 = t1 + sol1(c1, ty);
    }
    pair<int, int> t2 = {0, 0};
    int optc = -1;
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        pair<int, int> tmp = t1;
        tmp = tmp - sol1(c1, ty);
        pair<int, int> sing; sing.first = 1;
        sing.second = s[v] != s[c1]? 1 : 0;
        tmp = tmp + sing;
        for (int j = 0; j < child[c1].size(); j++) {
            int s1 = child[c1][j];
            tmp = tmp + sol1(s1, ty);
        }
        if (tmp > t2) {
            optc = i; t2 = tmp;
        }
    }
    if (t1 > t2 || optc == -1) {
        an[v].sel = -1;
        return an[v].val = t1;
    } else {
        an[v].sel = optc;
        return an[v].val = t2;
    }
}

void print(int v, int ty) {
    nn *an = ty == 1? an1 : an2;
    vector<int> *child = ty == 1? child1 : child2;
    if (an[v].sel == -1) {
        for (int i = 0; i < child[v].size(); i++) {
            print(child[v][i], ty);
        }
    } else {
        for (int i = 0; i < child[v].size(); i++) {
            if (i != an[v].sel) print(child[v][i], ty);
        }
        int c1 = child[v][an[v].sel];
        printf("%d %d\n", v, c1);
        for (int i = 0; i < child[c1].size(); i++) {
            print(child[c1][i], ty);
        }
    }
}

void printAns(int idx, int ty) {
    if (ty == 1) {
        print(rr[idx], ty);
    } else {
        printf("%d %d\n", noEd[idx].first, noEd[idx].second);
        for (int i = 0; i < ty2[idx].invl.size(); i++) {
            int root = ty2[idx].invl[i];
            print(root, ty);
        }
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &f[i], &s[i]), adj[i].push_back(f[i]), adj[f[i]].push_back(i);

    mem(vist);
    cc = 0;
    ini();
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr[cc] = i;
            dfs1(i);
            cc++;
        }
    }
    cn = cc;
    for (int i = 1; i <= n; i++) an1[i].val = {-1, -1}, an2[i].val = {-1, -1};
    for (int i = 0; i < cn; i++) {
        sol1(rr[i], 1);
    }
    unordered_set<int> noSel;
    for (int i = 0; i < cn; i++) {
        noSel.insert(noEd[i].first), noSel.insert(noEd[i].second);
    }
    for (int i = 1; i <= n; i++) {
        if (noSel.find(i) != noSel.end()) continue;
        for (int j = 0; j < adj[i].size(); j++) {
            int u = adj[i][j];
            if (noSel.find(u) != noSel.end()) continue;
            if (i < u) adj2[i].push_back(u), adj[u].push_back(i);
        }
    }
    mem(vist), cc = 0;
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr2[cc] = i;
            dfs2(i);
            cc++;
        }
    }
    cn2 = cc;
    for (int i = 0; i < cn2; i++) {
        sol1(rr2[i], 2);
    }
    unordered_map<int, int> soi;
    //dbg(cn);
    for (int i = 0; i < cn; i++) {
        //printf("%d %d\n", noEd[i].first, noEd[i].second);
        int v = noEd[i].first;
        int p = findSet(v);
        soi[p] = i;
        ty2[i].val.first = 1;
        ty2[i].val.second = s[noEd[i].first] != s[noEd[i].second]? 1 : 0;
    }
    for (int i = 0; i < cn2; i++) {
        pair<int, int> val = sol1(rr2[i], 2);
        assert(soi.find(findSet(rr2[i])) != soi.end());
        int idx = soi[findSet(rr2[i])];
        ty2[idx].val = ty2[idx].val + val;
        ty2[idx].invl.push_back(rr2[i]);
    }
    pair<int, int> ans = {0, 0};
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) ans = ans + t1;
        else ans = ans + t2;
    }
    printf("%d %d\n", ans.first, ans.second);
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) printAns(i, 1);
        else printAns(i, 2);
    }
    return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string.h>
#include <limits.h>
#include <cfloat>
#include <stack>
#include <queue>
#include <list>
#include <set>
#include <map>
#include <bitset>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define mem(flag) memset(flag,0,sizeof(flag))
#define Mem(flag) memset(flag,-1,sizeof(flag))
#define read(a) a=getInt()
#define En printf("\n")
#define P(a) printf("%d ",a)
#define Pn(a) printf("%d\n",a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define rep(flag, a, b) for (int flag = (a); flag <= (b); ++flag)
#define Rep(flag, a, b) for (int flag = (a); flag >= (b); --flag)

const int MAX = 1e5 + 20;
vector<int> adj[MAX], adj2[MAX];
struct node {
    int parent;
    int ranks;
};
node trees[MAX];
int n,i,j,k;
void ini() {
    for(i=1;i<=n;i++)
        trees[i].parent=i,trees[i].ranks=0;
}
int findSet(int x) {
    if(trees[x].parent!=x) return trees[x].parent=findSet(trees[x].parent);
    else return x;
}
void unions(int x,int y) {
    int rootX=findSet(x),rootY=findSet(y);
    if(rootX==rootY) return;
    if(trees[rootX].ranks>trees[rootY].ranks) swap(rootX,rootY);
    trees[rootX].parent=rootY;
    if(trees[rootX].ranks==trees[rootY].ranks) trees[rootY].ranks++;
}

pair<int, int> noEd[MAX]; int nonum;
struct nn {
    pair<int, int> val; int sel;
};
nn an1[MAX];
nn an2[MAX];
struct nn2 {
    vector<int> invl;
    pair<int, int> val;
};
nn2 ty2[MAX];
int rr[MAX], rr2[MAX], cn, cn2;
vector<int> child1[MAX], child2[MAX];
int f[MAX], s[MAX];
bool vist[MAX];
int dep[MAX];
int cc;

void dfs1(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj[v].size(); i++) {
        int u = adj[v][i];
        if (!vist[u]) {
            child1[v].push_back(u);
            unions(u, v);
            dep[u] = dep[v] + 1;
            dfs1(u);
        } else if (dep[v] < dep[u]) noEd[cc] = {v, u};
    }
}
void dfs2(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj2[v].size(); i++) {
        int u = adj2[v][i];
        if (!vist[u]) {
            child2[v].push_back(u);
            dfs2(u);
        }
    }
}

inline pair<int, int> operator + (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first + b.first, a.second + b.second};
}
inline pair<int, int> operator - (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first - b.first, a.second - b.second};
}
inline bool operator > (const pair<int, int> &a, const pair<int, int> &b) {
    if (a.first != b.first) return a.first > b.first;
    else return a.second > b.second;
}

pair<int, int> sol1(int v, int ty) {
    vector<int> *child; nn *an;
    if (ty == 1) child = child1, an = an1;
    else child = child2, an = an2;
    if (an[v].val != (pair<int, int>) {-1, -1}) return an[v].val;
    pair<int, int> t1 = {0, 0};
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        t1 = t1 + sol1(c1, ty);
    }
    pair<int, int> t2 = {0, 0};
    int optc = -1;
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        pair<int, int> tmp = t1;
        tmp = tmp - sol1(c1, ty);
        pair<int, int> sing; sing.first = 1;
        sing.second = s[v] != s[c1]? 1 : 0;
        tmp = tmp + sing;
        for (int j = 0; j < child[c1].size(); j++) {
            int s1 = child[c1][j];
            tmp = tmp + sol1(s1, ty);
        }
        if (tmp > t2) {
            optc = i; t2 = tmp;
        }
    }
    if (t1 > t2 || optc == -1) {
        an[v].sel = -1;
        return an[v].val = t1;
    } else {
        an[v].sel = optc;
        return an[v].val = t2;
    }
}

void print(int v, int ty) {
    nn *an = ty == 1? an1 : an2;
    vector<int> *child = ty == 1? child1 : child2;
    if (an[v].sel == -1) {
        for (int i = 0; i < child[v].size(); i++) {
            print(child[v][i], ty);
        }
    } else {
        for (int i = 0; i < child[v].size(); i++) {
            if (i != an[v].sel) print(child[v][i], ty);
        }
        int c1 = child[v][an[v].sel];
        printf("%d %d\n", v, c1);
        for (int i = 0; i < child[c1].size(); i++) {
            print(child[c1][i], ty);
        }
    }
}

void printAns(int idx, int ty) {
    if (ty == 1) {
        print(rr[idx], ty);
    } else {
        printf("%d %d\n", noEd[idx].first, noEd[idx].second);
        for (int i = 0; i < ty2[idx].invl.size(); i++) {
            int root = ty2[idx].invl[i];
            print(root, ty);
        }
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &f[i], &s[i]), adj[i].push_back(f[i]), adj[f[i]].push_back(i);

    mem(vist);
    cc = 0;
    ini();
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr[cc] = i;
            dep[i] = 0;
            dfs1(i);
            cc++;
        }
    }
    cn = cc;
    for (int i = 1; i <= n; i++) an1[i].val = {-1, -1}, an2[i].val = {-1, -1};
    for (int i = 0; i < cn; i++) {
        sol1(rr[i], 1);
    }
    unordered_set<int> noSel;
    for (int i = 0; i < cn; i++) {
        noSel.insert(noEd[i].first), noSel.insert(noEd[i].second);
    }
    for (int i = 1; i <= n; i++) {
        if (noSel.find(i) != noSel.end()) continue;
        for (int j = 0; j < adj[i].size(); j++) {
            int u = adj[i][j];
            if (noSel.find(u) != noSel.end()) continue;
            if (i < u) adj2[i].push_back(u), adj2[u].push_back(i);
        }
    }
    mem(vist), cc = 0;
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr2[cc] = i;
            dfs2(i);
            cc++;
        }
    }
    cn2 = cc;
    for (int i = 0; i < cn2; i++) {
        sol1(rr2[i], 2);
    }
    unordered_map<int, int> soi;
    //dbg(cn);
    for (int i = 0; i < cn; i++) {
        //printf("%d %d\n", noEd[i].first, noEd[i].second);
        int v = noEd[i].first;
        int p = findSet(v);
        soi[p] = i;
        ty2[i].val.first = 1;
        ty2[i].val.second = s[noEd[i].first] != s[noEd[i].second]? 1 : 0;
    }
    for (int i = 0; i < cn2; i++) {
        pair<int, int> val = sol1(rr2[i], 2);
        assert(soi.find(findSet(rr2[i])) != soi.end());
        int idx = soi[findSet(rr2[i])];
        assert(idx < cn);
        ty2[idx].val = ty2[idx].val + val;
        ty2[idx].invl.push_back(rr2[i]);
    }
    pair<int, int> ans = {0, 0};
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) ans = ans + t1;
        else ans = ans + t2;
    }
    printf("%d %d\n", ans.first, ans.second);
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) printAns(i, 1);
        else printAns(i, 2);
    }
    return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string.h>
#include <limits.h>
#include <cfloat>
#include <stack>
#include <queue>
#include <list>
#include <set>
#include <map>
#include <bitset>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define mem(flag) memset(flag,0,sizeof(flag))
#define Mem(flag) memset(flag,-1,sizeof(flag))
#define read(a) a=getInt()
#define En printf("\n")
#define P(a) printf("%d ",a)
#define Pn(a) printf("%d\n",a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define rep(flag, a, b) for (int flag = (a); flag <= (b); ++flag)
#define Rep(flag, a, b) for (int flag = (a); flag >= (b); --flag)

const int MAX = 1e5 + 20;
vector<int> adj[MAX], adj2[MAX];
struct node {
    int parent;
    int ranks;
};
node trees[MAX];
int n,i,j,k;
void ini() {
    for(i=1;i<=n;i++)
        trees[i].parent=i,trees[i].ranks=0;
}
int findSet(int x) {
    if(trees[x].parent!=x) return trees[x].parent=findSet(trees[x].parent);
    else return x;
}
void unions(int x,int y) {
    int rootX=findSet(x),rootY=findSet(y);
    if(rootX==rootY) return;
    if(trees[rootX].ranks>trees[rootY].ranks) swap(rootX,rootY);
    trees[rootX].parent=rootY;
    if(trees[rootX].ranks==trees[rootY].ranks) trees[rootY].ranks++;
}

pair<int, int> noEd[MAX]; int nonum;
struct nn {
    pair<int, int> val; int sel;
};
nn an1[MAX];
nn an2[MAX];
struct nn2 {
    vector<int> invl;
    pair<int, int> val;
};
nn2 ty2[MAX];
int rr[MAX], rr2[MAX], cn, cn2;
vector<int> child1[MAX], child2[MAX];
int f[MAX], s[MAX];
bool vist[MAX];
int dep[MAX];
int cc;

void dfs1(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj[v].size(); i++) {
        int u = adj[v][i];
        if (!vist[u]) {
            child1[v].push_back(u);
            unions(u, v);
            dep[u] = dep[v] + 1;
            dfs1(u);
        } else if (dep[v] < dep[u]) noEd[cc] = {v, u};
    }
}
void dfs2(int v) {
    vist[v] = 1;
    for (int i = 0; i < adj2[v].size(); i++) {
        int u = adj2[v][i];
        if (!vist[u]) {
            child2[v].push_back(u);
            dfs2(u);
        }
    }
}

inline pair<int, int> operator + (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first + b.first, a.second + b.second};
}
inline pair<int, int> operator - (const pair<int, int> &a, const pair<int, int> &b) {
    return {a.first - b.first, a.second - b.second};
}
inline bool operator > (const pair<int, int> &a, const pair<int, int> &b) {
    if (a.first != b.first) return a.first > b.first;
    else return a.second > b.second;
}

pair<int, int> sol1(int v, int ty) {
    vector<int> *child; nn *an;
    if (ty == 1) child = child1, an = an1;
    else child = child2, an = an2;
    if (an[v].val != (pair<int, int>) {-1, -1}) return an[v].val;
    pair<int, int> t1 = {0, 0};
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        t1 = t1 + sol1(c1, ty);
    }
    pair<int, int> t2 = {0, 0};
    int optc = -1;
    for (int i = 0; i < child[v].size(); i++) {
        int c1 = child[v][i];
        pair<int, int> tmp = t1;
        tmp = tmp - sol1(c1, ty);
        pair<int, int> sing; sing.first = 1;
        sing.second = s[v] != s[c1]? 1 : 0;
        tmp = tmp + sing;
        for (int j = 0; j < child[c1].size(); j++) {
            int s1 = child[c1][j];
            tmp = tmp + sol1(s1, ty);
        }
        if (tmp > t2) {
            optc = i; t2 = tmp;
        }
    }
    if (t1 > t2 || optc == -1) {
        an[v].sel = -1;
        return an[v].val = t1;
    } else {
        an[v].sel = optc;
        return an[v].val = t2;
    }
}

void print(int v, int ty) {
    nn *an = ty == 1? an1 : an2;
    vector<int> *child = ty == 1? child1 : child2;
    if (an[v].sel == -1) {
        for (int i = 0; i < child[v].size(); i++) {
            print(child[v][i], ty);
        }
    } else {
        for (int i = 0; i < child[v].size(); i++) {
            if (i != an[v].sel) print(child[v][i], ty);
        }
        int c1 = child[v][an[v].sel];
        printf("%d %d\n", v, c1);
        for (int i = 0; i < child[c1].size(); i++) {
            print(child[c1][i], ty);
        }
    }
}

void printAns(int idx, int ty) {
    if (ty == 1) {
        print(rr[idx], ty);
    } else {
        printf("%d %d\n", noEd[idx].first, noEd[idx].second);
        for (int i = 0; i < ty2[idx].invl.size(); i++) {
            int root = ty2[idx].invl[i];
            print(root, ty);
        }
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &f[i], &s[i]), adj[i].push_back(f[i]), adj[f[i]].push_back(i);

    mem(vist);
    cc = 0;
    ini();
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr[cc] = i;
            dep[i] = 0;
            dfs1(i);
            cc++;
        }
    }
    cn = cc;
    for (int i = 1; i <= n; i++) an1[i].val = {-1, -1}, an2[i].val = {-1, -1};
    for (int i = 0; i < cn; i++) {
        sol1(rr[i], 1);
    }
    unordered_set<int> noSel;
    for (int i = 0; i < cn; i++) {
        noSel.insert(noEd[i].first), noSel.insert(noEd[i].second);
    }
    for (int i = 1; i <= n; i++) {
        if (noSel.find(i) != noSel.end()) continue;
        for (int j = 0; j < adj[i].size(); j++) {
            int u = adj[i][j];
            if (noSel.find(u) != noSel.end()) continue;
            if (i < u) adj2[i].push_back(u), adj[u].push_back(i);
        }
    }
    mem(vist), cc = 0;
    for (int i = 1; i <= n; i++) {
        if (!vist[i]) {
            rr2[cc] = i;
            dfs2(i);
            cc++;
        }
    }
    cn2 = cc;
    for (int i = 0; i < cn2; i++) {
        sol1(rr2[i], 2);
    }
    unordered_map<int, int> soi;
    //dbg(cn);
    for (int i = 0; i < cn; i++) {
        //printf("%d %d\n", noEd[i].first, noEd[i].second);
        int v = noEd[i].first;
        int p = findSet(v);
        soi[p] = i;
        ty2[i].val.first = 1;
        ty2[i].val.second = s[noEd[i].first] != s[noEd[i].second]? 1 : 0;
    }
    for (int i = 0; i < cn2; i++) {
        pair<int, int> val = sol1(rr2[i], 2);
        assert(soi.find(findSet(rr2[i])) != soi.end());
        int idx = soi[findSet(rr2[i])];
        ty2[idx].val = ty2[idx].val + val;
        ty2[idx].invl.push_back(rr2[i]);
    }
    pair<int, int> ans = {0, 0};
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) ans = ans + t1;
        else ans = ans + t2;
    }
    printf("%d %d\n", ans.first, ans.second);
    for (int i = 0; i < cn; i++) {
        pair<int, int> t1 = an1[rr[i]].val;
        pair<int, int> t2 = ty2[i].val;
        if (t1 > t2) printAns(i, 1);
        else printAns(i, 2);
    }
    return 0;
}


//��
#include<iostream>
#include<cstdio>
#include<fstream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int N = 111111;
struct node{
	int x,y,z;
}f[N],g[N],dp[N],ans,tmp,q;
node operator+(node a,node b){
	node c;
	c.x=a.x+b.x;
	c.y=a.y+b.y;
	c.z=0;
	return c;
}
node operator-(node a,node b){
	node c;
	c.x=a.x-b.x;
	c.y=a.y-b.y;
	c.z=0;
	return c;
}
bool operator<(node a,node b){
	if(a.x!=b.x)
		return a.x<b.x;
	return a.y<b.y;
}
void prt(node a){
	cout<<a.x<<' '<<a.y<<' '<<a.z<<endl;
}
int n,a[N],b[N],vis[N],t[N],p[N];
vector<int> v[N];
void dfs(int u,int fa){
	vis[u]=1;
	int i,w,sz=v[u].size();
	node ff=q,gg=q;
	f[u]=q,g[u]=q;
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			//cout<<u<<w<<endl;
			dfs(w,fa);
			ff=ff+f[w];
		}
	}
	g[u]=ff;
	f[u]=g[u];
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			gg=ff-f[w]+g[w];
			gg.x++,gg.y+=b[u]^b[w],gg.z=w;
			if(f[u]<gg)
				f[u]=gg;
		}
	}
	//cout<<u<<endl;
	//prt(ff),prt(f[u]),prt(g[u]);
}
vector<pair<int,int> > h;
void getans(int u,int fa,bool isf){
	int i,w,sz=v[u].size();
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			if(isf&&w==f[u].z){
				h.push_back(make_pair(u,w));
				getans(w,fa,0);
			}
			else
				getans(w,fa,1);
		}
	}
}
int main()
{
	q.x=0,q.y=0,q.z=0;
	ans=q;
	int i,j,k,w;
	scanf("%d",&n);
	for(i=1;i<=n;i=i+1){
		scanf("%d%d",a+i,b+i);
		v[a[i]].push_back(i);
		b[i]--;
	}
	for(i=1;i<=n;i=i+1){
		if(vis[i])
			continue;
		j=0;
		w=i;
		while(!p[w]){
			t[j++]=w;
			p[w]=1;
			w=a[w];
		}
		reverse(t,t+j);
		for(k=0;t[k]!=w;k=k+1);
		dfs(t[0],t[k]);
		tmp=g[t[0]];
		dfs(t[0],t[0]);
		tmp=tmp+g[t[k]];
		tmp.x++,tmp.y+=b[t[0]]^b[t[k]];
		//prt(tmp),prt(f[t[0]]);
		if(f[t[0]]<tmp){
			ans=ans+tmp;
			getans(t[k],t[k],0);
			dfs(t[0],t[k]);
			getans(t[0],t[k],0);
			h.push_back(make_pair(t[0],t[k]));
		}
		else{
			ans=ans+f[t[0]];
			getans(t[0],t[0],1);
		}

	}
	cout<<ans.x<<' '<<ans.y<<endl;
	j=h.size();
	while(j--){
		cout<<h[j].first<<' '<<h[j].second<<endl;
	}
	return 0;
}
/*
5
5 2
3 2
5 1
2 1
4 2

6
5 2
3 2
5 1
2 1
4 2
3 1

8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1

*/


//��
#include<iostream>
#include<cstdio>
#include<fstream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int N = 111111;
struct node{
	int x,y,z;
}f[N],g[N],dp[N],ans,tmp,q;
node operator+(node a,node b){
	node c;
	c.x=a.x+b.x;
	c.y=a.y+b.y;
	c.z=0;
	return c;
}
node operator-(node a,node b){
	node c;
	c.x=a.x-b.x;
	c.y=a.y-b.y;
	c.z=0;
	return c;
}
bool operator<(node a,node b){
	if(a.x!=b.x)
		return a.x<b.x;
	return a.y<b.y;
}
void prt(node a){
	cout<<a.x<<' '<<a.y<<' '<<a.z<<endl;
}
int n,a[N],b[N],vis[N],t[N],p[N];
vector<int> v[N];
void dfs(int u,int fa){
	vis[u]=1;
	int i,w,sz=v[u].size();
	node ff=q,gg=q;
	f[u]=q,g[u]=q;
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			//cout<<u<<w<<endl;
			dfs(w,fa);
			ff=ff+f[w];
		}
	}
	g[u]=ff;
	f[u]=g[u];
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			gg=ff-f[w]+g[w];
			gg.x++,gg.y+=b[u]^b[w],gg.z=w;
			if(f[u]<gg)
				f[u]=gg;
		}
	}
	//cout<<u<<endl;
	//prt(ff),prt(f[u]),prt(g[u]);
}
vector<pair<int,int> > h;
void getans(int u,int fa,bool isf){
	int i,w,sz=v[u].size();
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			if(isf&&w==f[u].z){
				h.push_back(make_pair(u,w));
				getans(w,fa,0);
			}
			else
				getans(w,fa,1);
		}
	}
}
int main()
{
	q.x=0,q.y=0,q.z=0;
	ans=q;
	int i,j,k,w;
	scanf("%d",&n);
	for(i=1;i<=n;i=i+1){
		scanf("%d%d",a+i,b+i);
		v[a[i]].push_back(i);
		b[i]--;
	}
	for(i=1;i<=n;i=i+1){
		if(vis[i])
			continue;
		j=0;
		w=i;
		while(!p[w]){
			t[j++]=w;
			p[w]=1;
			w=a[w];
		}
		reverse(t,t+j);
		for(k=0;t[k]!=w;k=k+1);
		dfs(t[0],t[k]);
		tmp=g[t[0]];
		dfs(t[0],t[0]);
		tmp=tmp+g[t[k]];
		tmp.x++,tmp.y+=b[t[0]]^b[t[k]];
		//prt(tmp),prt(f[t[0]]);
		if(f[t[0]]<tmp){
			ans=ans+tmp;
			getans(t[k],t[k],0);
			dfs(t[0],t[k]);
			getans(t[0],t[k],1);
			h.push_back(make_pair(t[0],t[k]));
		}
		else{
			ans=ans+f[t[0]];
			getans(t[0],t[0],1);
		}

	}
	cout<<ans.x<<' '<<ans.y<<endl;
	j=h.size();
	while(j--){
		cout<<h[j].first<<' '<<h[j].second<<endl;
	}
	return 0;
}
/*
5
5 2
3 2
5 1
2 1
4 2

6
5 2
3 2
5 1
2 1
4 2
3 1

8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1

*/


//��
#include<iostream>
#include<cstdio>
#include<fstream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int N = 111111;
struct node{
	int x,y,z;
}f[N],g[N],dp[N],ans,tmp,q;
node operator+(node a,node b){
	node c;
	c.x=a.x+b.x;
	c.y=a.y+b.y;
	c.z=0;
	return c;
}
node operator-(node a,node b){
	node c;
	c.x=a.x-b.x;
	c.y=a.y-b.y;
	c.z=0;
	return c;
}
bool operator<(node a,node b){
	if(a.x!=b.x)
		return a.x<b.x;
	return a.y<b.y;
}
void prt(node a){
	cout<<a.x<<' '<<a.y<<' '<<a.z<<endl;
}
int n,a[N],b[N],vis[N],t[N],p[N];
vector<int> v[N];
void dfs(int u,int fa){
	vis[u]=1;
	int i,w,sz=v[u].size();
	node ff=q,gg=q;
	f[u]=q,g[u]=q;
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			//cout<<u<<w<<endl;
			dfs(w,fa);
			ff=ff+f[w];
		}
	}
	g[u]=ff;
	f[u]=g[u];
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			gg=ff-f[w]+g[w];
			gg.x++,gg.y+=b[u]^b[w],gg.z=w;
			if(f[u]<gg)
				f[u]=gg;
		}
	}
	//cout<<u<<endl;
	//prt(ff),prt(f[u]),prt(g[u]);
}
vector<pair<int,int> > h;
void getans(int u,int fa,bool isf){
	int i,w,sz=v[u].size();
	for(i=0;i<sz;i=i+1){
		w=v[u][i];
		if(w!=fa){
			if(isf&&w==f[u].z){
				h.push_back(make_pair(u,w));
				getans(w,fa,0);
			}
			else
				getans(w,fa,1);
		}
	}
}
int main()
{
	q.x=0,q.y=0,q.z=0;
	ans=q;
	int i,j,k,w;
	scanf("%d",&n);
	for(i=1;i<=n;i=i+1){
		scanf("%d%d",a+i,b+i);
		v[a[i]].push_back(i);
		b[i]--;
	}
	for(i=1;i<=n;i=i+1){
		if(vis[i])
			continue;
		j=0;
		w=i;
		while(!p[w]){
			t[j++]=w;
			p[w]=1;
			w=a[w];
		}
		reverse(t,t+j);
		for(k=0;t[k]!=w;k=k+1);
		dfs(t[0],t[k]);
		tmp=g[t[0]];
		dfs(t[0],t[0]);
		tmp=tmp+g[t[k]];
		tmp.x++,tmp.y+=b[t[0]]^b[t[k]];
		//prt(tmp),prt(f[t[0]]);
		if(f[t[0]]<tmp){
			ans=ans+tmp;
			getans(t[k],t[0],0);
			dfs(t[0],t[k]);
			getans(t[0],t[k],0);
			h.push_back(make_pair(t[0],t[k]));
		}
		else{
			ans=ans+f[t[0]];
			getans(t[0],t[0],1);
		}

	}
	cout<<ans.x<<' '<<ans.y<<endl;
	j=h.size();
	while(j--){
		cout<<h[j].first<<' '<<h[j].second<<endl;
	}
	return 0;
}
/*
5
5 2
3 2
5 1
2 1
4 2

6
5 2
3 2
5 1
2 1
4 2
3 1

8
2 2
3 2
5 1
3 1
6 1
5 1
8 2
7 1

*/


#include<bits/stdc++.h>
#define pb push_back
#define rep(i,a,b) for (int i=(a); i<=int(b); i++)
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int maxn = 100009;
int g[maxn], rt, _[maxn], s[maxn], v[maxn], n;
vector<int> e[maxn]; vector<pii> p, q;
pii f[maxn][2], ans;
void dfs(int u) {
	f[u][0] = f[u][1] = pii(0, 0); g[u] = 0; v[u] = 1;
	for (int v : e[u]) {
		if (v == rt) continue;
		dfs(v); f[u][0].x += f[v][1].x; f[u][0].y += f[v][1].y;
		pii t(1 - f[v][1].x + f[v][0].x, (s[u] ^ s[v]) - f[v][1].y + f[v][0].y);
		if (f[u][1] < t) f[u][1] = t, g[u] = v;
	}
	f[u][1].x += f[u][0].x; f[u][1].y += f[u][0].y;
}
void update(int u, int b) {
	for (int v : e[u]) {
		if (v == rt) continue;
		if (!b || g[u] != v) update(v, 1);
		else p.pb(pii(u, v)), update(v, 0);
	}
}
void solve(int u) {
	for (; !v[u]; u = _[u]) v[u] = 1;
	pii res(0, 0);
	rep (t, 0, 1) {
		rt = u; dfs(u);
		if (res < f[u][1]) res = f[u][1], p.clear(), update(u, 1);
		u = _[u];
	}
	for (pii v : p) q.pb(v);
	ans.x += res.x; ans.y += res.y;
}
int main() {
	scanf("%d", &n);
	rep (i, 1, n) scanf("%d%d", &_[i], &s[i]), --s[i], e[_[i]].pb(i);
	rep (i, 1, n) if (!v[i]) solve(i);
	printf("%d %d\n", ans.x, ans.y);
	for (pii v : q) printf("%d %d\n", v.x, v.y);
	return 0;
}


#include <cstdio>
#include <vector>
#include <algorithm>
#pragma comment(linker, "/STACK:16000000")
using namespace std;

typedef pair <int, int> ii;

const int Inf = 1000000000;
const int Maxn = 100005;

int n;
int f[Maxn], s[Maxn];
vector <int> neigh[Maxn];
bool taken[Maxn];
vector <int> comp;
int root, mid;
ii edge;
ii dp[Maxn][2];
int from[Maxn];
ii res = ii(0, 0);
vector <ii> rpairs;

ii Max(ii a, ii b)
{
	if (a.first != b.first) return a.first > b.first? a: b;
	return a.second > b.second? a: b;
}

ii Add(ii a, ii b) { return ii(a.first + b.first, a.second + b.second); }

ii Sub(ii a, ii b) { return ii(a.first - b.first, a.second - b.second); }

void getComp(int v)
{
	comp.push_back(v); taken[v] = true;
	while (!taken[f[v]]) { v = f[v]; comp.push_back(v); taken[v] = true; }
	root = v; mid = f[v]; edge = ii(1, s[root] != s[mid]);
	for (int i = 0; i < comp.size(); i++) {
		int v = comp[i];
		for (int j = 0; j < neigh[v].size(); j++) {
			int u = neigh[v][j];
			if (!taken[u]) { comp.push_back(u); taken[u] = true; }
		}
	}
}

void Calc(int v)
{
	taken[v] = true;
	dp[v][0] = dp[v][1] = ii(0, 0);
	ii mx = ii(-Inf, -Inf);
	for (int i = 0; i < neigh[v].size(); i++) {
		int u = neigh[v][i];
		if (taken[u]) continue;
		Calc(u);
		dp[v][0] = Add(dp[v][0], Max(dp[u][0], dp[u][1]));
		mx = Max(mx, Add(ii(1, s[v] != s[u]), Sub(dp[u][0], Max(dp[u][0], dp[u][1]))));
	}
	dp[v][1] = Add(dp[v][0], mx);
}

void Get(int v, int f)
{
	taken[v] = true;
	int spec;
	if (f == 0) spec = -1;
	else {
		for (int i = 0; i < neigh[v].size(); i++) {
			int u = neigh[v][i];
			if (taken[u]) continue;
			if (Sub(dp[v][1], dp[v][0]) == Add(ii(1, s[v] != s[u]), Sub(dp[u][0], Max(dp[u][0], dp[u][1])))) {
				spec = u; break;
			}
		}
		rpairs.push_back(ii(v, spec));
	}
	for (int i = 0; i < neigh[v].size(); i++) {
		int u = neigh[v][i];
		if (taken[u]) continue;
		if (spec == u) Get(u, 0);
		else if (Max(dp[u][0], dp[u][1]) != dp[u][0]) Get(u, 1);
			 else Get(u, 0);
	}
}

void Process(int i)
{
	comp.clear(); getComp(i);
	for (int i = 0; i < comp.size(); i++) taken[comp[i]] = false;
	Calc(root);
	ii a = Max(dp[root][0], dp[root][1]);
	for (int i = 0; i < comp.size(); i++) taken[comp[i]] = false;
	taken[root] = taken[mid] = true; Calc(root); Calc(mid);
	ii b = Add(edge, Add(dp[root][0], dp[mid][0]));
	if (Max(a, b) != b) {
		for (int i = 0; i < comp.size(); i++) taken[comp[i]] = false;
		Calc(root);
		for (int i = 0; i < comp.size(); i++) taken[comp[i]] = false;
		if (Max(dp[root][0], dp[root][1]) != dp[root][0]) Get(root, 1);
		else Get(root, 0);
	} else {
		for (int i = 0; i < comp.size(); i++) taken[comp[i]] = false;
		taken[root] = taken[mid] = true;
		rpairs.push_back(ii(root, mid)); Get(root, 0); Get(mid, 0);
	}
	res = Add(res, Max(a, b));
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d", &f[i], &s[i]);
		neigh[f[i]].push_back(i);
	}
	for (int i = 1; i <= n; i++) if (neigh[i].empty() && !taken[i])
		Process(i);
	for (int i = 1; i <= n; i++) if (!taken[i])
		Process(i);
	printf("%d %d\n", res.first, res.second);
	for (int i = 0; i < rpairs.size(); i++)
		printf("%d %d\n", rpairs[i].first, rpairs[i].second);
	return 0;
}