#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_DEPRECATE

#include <algorithm>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>

using namespace std;

#pragma comment(linker, "/STACK:128000000")

template<class T> inline T sqr (T x) {return x * x;}

typedef long long int64;
typedef unsigned long long uint64;
typedef pair<int, int> pii;
typedef pair<int, pii> pip;
typedef pair<pii, int> ppi;
typedef pair<int64, int64> pii64;
typedef pair<double, double> pdd;
typedef pair<string, int> psi;
typedef pair<int, string> pis;
#define eps  1e-8
#define inf  0x7f7f7f7f
#define MP make_pair
#define sfind(S, C) int((S).find(C))
#define sz(C) (int)((C).size())
#define all(C) (C).begin(), (C).end()
#define TASK "test"
#define RR 151

int n;
int g[1 << 17];
vector< vector<int> > a;
int cl[1 << 17];
char gt[1 << 17];
char used[1 << 17];

void dfs0 (int v, int col, vector<int> &comp) {
    cl[v] = col;
    comp.push_back(v);
    for (int i = 0; i < sz(a[v]); ++i) {
        int to = a[v][i];
        if (cl[to] > col)
            dfs0(to, col, comp);
    }
}

void dfs1 (int v, vector<int> &order) {
    vector<int> tmp;
    for (int i = v; !used[i]; i = g[i]) {
        used[i] = true;
        tmp.push_back(i);
    }
    reverse(all(tmp));
    order.insert(order.end(), all(tmp));
}

int main() {
#ifndef ONLINE_JUDGE
    freopen(TASK ".in", "r", stdin);
    freopen(TASK ".out", "w", stdout);
#endif

    scanf("%d", &n);
    a.resize(n);
    for (int i = 0, x; i < n; ++i) {
        scanf("%d", &x); --x;
        g[i] = x;
        gt[x] = true;
        a[i].push_back(x);
        a[x].push_back(i);
    }
    memset(cl, 127, sizeof cl);
    int cols = 0;
    vector<int> comps, order;
    vector<pii> ans;
    for (int i = 0; i < n; ++i) {
        if (cl[i] < inf) continue;
        comps.push_back(i);
        vector<int> comp;
        dfs0(i, cols++, comp);
        for (int j = 0; j < sz(comp); ++j) {
            int v = comp[j];
            if (used[v]) continue;
            dfs1(v, order);
        }
        int cur = order[0];
        for (int j = 1; j < sz(order); ++j) {
            int v = order[j];
            if (!gt[v])
                ans.push_back(MP(cur, v));
        }
    }
    if (sz(comps) > 1) {
        for (int i = 0; i < sz(comps); ++i) {
            int x = comps[i], y = comps[(i + 1) % sz(comps)];
            ans.push_back(MP(x, y));
        }
    }
    printf("%d\n", sz(ans));
    for (int i = 0; i < sz(ans); ++i)
        printf("%d %d\n", ans[i].first + 1, ans[i].second + 1);

    return 0;
}


#include <iostream>
#include <vector>
using namespace std;

int n;
int nxt[100001];
int deg[100001];
bool was[100001];
vector<pair<int, int>> q;
vector<int> cycles;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> nxt[i], ++deg[nxt[i]];
    for (int i = 1; i <= n; ++i)
        if (!deg[i])
            q.push_back(make_pair(i, 0));
    for (int i = 0; i < q.size(); ++i)
    {
        int x = q[i].first;
        while (!was[x])
        {
            was[x] = true;
            q[i].second = x;
            x = nxt[x];
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        int x = i;
        if (!was[x])
            cycles.push_back(x);
        while (!was[x])
        {
            was[x] = true;
            x = nxt[x];
        }
    }
    if (q.empty() && cycles.size() == 1)
        return !printf("0\n");
    for (int i = 0; i < cycles.size(); ++i)
        q.push_back(make_pair(cycles[i], cycles[i]));
    cout << q.size() << endl;
    for (int i = 0; i < q.size(); ++i)
        cout << q[i].second << " " << q[(i + 1) % q.size()].first << endl;
    return 0;
}


#include <iostream>
#include <vector>
using namespace std;

int n;
int nxt[100001];
int deg[100001];
bool was[100001];
vector<pair<int, int>> q;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> nxt[i], ++deg[nxt[i]];
    for (int i = 1; i <= n; ++i)
        if (!deg[i])
            q.push_back(make_pair(i, 0));
    for (int i = 0; i < q.size(); ++i)
    {
        int x = q[i].first;
        while (!was[x])
        {
            was[x] = true;
            q[i].second = x;
            x = nxt[x];
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        int x = i;
        if (!was[x])
            q.push_back(make_pair(x, x));
        while (!was[x])
        {
            was[x] = true;
            x = nxt[x];
        }
    }
    cout << q.size() << endl;
    for (int i = 0; i < q.size(); ++i)
        cout << q[i].second << " " << q[(i + 1) % q.size()].first << endl;
    return 0;
}


#include <iostream>
#include <vector>
using namespace std;

int n;
int nxt[100001];
int deg[100001];
bool was[100001];
vector<pair<int, int>> q;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> nxt[i], ++deg[nxt[i]];
    for (int i = 1; i <= n; ++i)
        if (!deg[i])
            q.push_back(make_pair(i, 0));
    for (int i = 0; i < q.size(); ++i)
    {
        int x = q[i].first;
        while (!was[x])
        {
            was[x] = true;
            q[i].second = x;
            x = nxt[x];
        }
    }
    cout << q.size() << endl;
    for (int i = 0; i < q.size(); ++i)
        cout << q[i].second << " " << q[(i + 1) % q.size()].first << endl;
    return 0;
}


#include <cstdio>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;

const int MAXN = 100010 + 1;

int n, room[MAXN], room0, bfs[MAXN], bfs0, start[MAXN], end[MAXN];
bool in[MAXN], tag[MAXN];
vector <int> forward[MAXN], backward[MAXN], nodeList[MAXN];
vector < pair <int, int> > output;

inline void BFS(int node, int color) {
	bfs0 = 1;
	bfs[0] = node;
	room[node] = color;
	for (int oo = 0; oo < bfs0; oo++) {
		int o = bfs[oo];
		nodeList[color].push_back(o);
//printf("add %d -> %d\n", o, color);
		for (int ii = 0; ii < (int)forward[o].size(); ii++) {
			int i = forward[o][ii];
			if (room[i] != -1) continue;
			room[i] = color;
			bfs[bfs0++] = i;
		}
		for (int ii = 0; ii < (int)backward[o].size(); ii++) {
			int i = backward[o][ii];
			if (room[i] != -1) continue;
			room[i] = color;
			bfs[bfs0++] = i;
		}
	}
}

inline void DFS(int node, int color) {
	if (tag[node]) return ;
	tag[node] = true;
	end[color] = node;
	for (int ii = 0; ii < (int)forward[node].size(); ii++) {
		DFS(forward[node][ii], color);
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int j;
		scanf("%d", &j);
		j--;
		forward[i].push_back(j);
		backward[j].push_back(i);
		in[j] = true;
	}
	memset(room, 0xFF, sizeof(int) * n);
	room0 = 0;
	for (int i = 0; i < n; i++) {
		if (room[i] == -1) BFS(i, room0++);
	}
	memset(start, 0xFF, sizeof(int) * room0);
	memset(end, 0xFF, sizeof(int) * room0);
	memset(tag, 0, sizeof(bool) * n);
	bool flag = false;
	for (int o = 0; o < room0; o++) {
//putchar('\t');
		for (int ii = 0; ii < (int)nodeList[o].size(); ii++) {
//printf("%d ", nodeList[o][ii]);
			int i = nodeList[o][ii];
			if (in[i]) continue;
			if (start[o] == -1) {
				start[o] = i;
				DFS(i, o);
				flag = true;
			} else {
//printf("add (%d %d)\n", end[o], i);
				output.push_back(make_pair(end[o], i));
			}
		}
		if (start[o] == -1) {
			start[o] = nodeList[o][0];
			end[o] = nodeList[o][1];
		}
//putchar('\n');
	}
	if (room0 == 1 && !flag) {
		puts("0");
		return 0;
	}
//for (int i = 0; i < n; i++) printf("room[%d] = %d\n", i, room[i]);
//for (int i = 0; i < room0; i++) printf("start[%d] = %d end[%d] = %d\n", i, start[i], i, end[i]);
	for (int o = 0; o < room0; o++) {
		output.push_back(make_pair(end[o], start[(o + 1) % room0]));
	}
	printf("%d\n", (int)output.size());
	for (int i = 0; i < (int)output.size(); i++) {
		printf("%d %d\n", output[i].first + 1, output[i].second + 1);
	}
	return 0;
}


#include <cstdio>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;

const int MAXN = 100010 + 1;

int n, room[MAXN], room0, bfs[MAXN], bfs0, start[MAXN], end[MAXN];
bool in[MAXN], tag[MAXN];
vector <int> forward[MAXN], backward[MAXN], nodeList[MAXN];
vector < pair <int, int> > output;

inline void BFS(int node, int color) {
	bfs0 = 1;
	bfs[0] = node;
	room[node] = color;
	for (int oo = 0; oo < bfs0; oo++) {
		int o = bfs[oo];
		nodeList[color].push_back(o);
//printf("add %d -> %d\n", o, color);
		for (int ii = 0; ii < (int)forward[o].size(); ii++) {
			int i = forward[o][ii];
			if (room[i] != -1) continue;
			room[i] = color;
			bfs[bfs0++] = i;
		}
		for (int ii = 0; ii < (int)backward[o].size(); ii++) {
			int i = backward[o][ii];
			if (room[i] != -1) continue;
			room[i] = color;
			bfs[bfs0++] = i;
		}
	}
}

inline void DFS(int node, int color) {
	if (tag[node]) return ;
	tag[node] = true;
	end[color] = node;
	for (int ii = 0; ii < (int)forward[node].size(); ii++) {
		DFS(forward[node][ii], color);
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int j;
		scanf("%d", &j);
		j--;
		forward[i].push_back(j);
		backward[j].push_back(i);
		in[j] = true;
	}
	memset(room, 0xFF, sizeof(int) * n);
	room0 = 0;
	for (int i = 0; i < n; i++) {
		if (room[i] == -1) BFS(i, room0++);
	}
	memset(start, 0xFF, sizeof(int) * room0);
	memset(end, 0xFF, sizeof(int) * room0);
	memset(tag, 0, sizeof(bool) * n);
	for (int o = 0; o < room0; o++) {
//putchar('\t');
		for (int ii = 0; ii < (int)nodeList[o].size(); ii++) {
//printf("%d ", nodeList[o][ii]);
			int i = nodeList[o][ii];
			if (in[i]) continue;
			if (start[o] == -1) {
				start[o] = i;
				DFS(i, o);
			} else {
//printf("add (%d %d)\n", end[o], i);
				output.push_back(make_pair(end[o], i));
			}
		}
		if (start[o] == -1) {
			start[o] = nodeList[o][0];
			end[o] = nodeList[o][1];
		}
//putchar('\n');
	}
//for (int i = 0; i < n; i++) printf("room[%d] = %d\n", i, room[i]);
//for (int i = 0; i < room0; i++) printf("start[%d] = %d end[%d] = %d\n", i, start[i], i, end[i]);
	for (int o = 0; o < room0; o++) {
		output.push_back(make_pair(end[o], start[(o + 1) % room0]));
	}
	printf("%d\n", (int)output.size());
	for (int i = 0; i < (int)output.size(); i++) {
		printf("%d %d\n", output[i].first + 1, output[i].second + 1);
	}
	return 0;
}


#!/usr/bin/env python3

# Read data
n = int(input())
f = map(int, input().split())
f = [d-1 for d in f] # Make indices 0-based

# Determine in-degree of all the nodes
indegree = [0 for i in range(n)]
for i in range(n):
  indegree[f[i]] += 1

# Nodes with indegree = 0 will need to be an end-point of a new edge
endpoints = [i for i in range(n) if (indegree[i] == 0)]
nr_indegree_zero = len(endpoints)

# Determine which (hereto unvisited) nodes will be reached by these endpoints
unvisited = set(range(n))
reach = [None for i in range(n)]

def determine_reach(v):
  path = [v]
  while (v in unvisited):
    unvisited.remove(v)
    v = f[v]
    path.append(v)
  for i in path:
    reach[i] = v

for v in endpoints:
  determine_reach(v)

# The reached nodes form good start-points for the new edges
startpoints = [reach[v] for v in endpoints]

# Check for isolated cycles that are not connected to the rest of the graph
nr_cycles = 0
while len(unvisited) > 0:
  # Select a node from the unvisited set (without removing it)
  v = unvisited.pop()
  unvisited.add(v)

  nr_cycles += 1
  determine_reach(v)
  endpoints.append(v)
  startpoints.append(reach[v])

# Special case: no indegree 0 nodes and only 1 cycle
if (nr_indegree_zero == 0) and (nr_cycles == 1):
  # No edges need to be added
  print(0)
  exit()

# Rotate the lists to each start point connects to the end-point of the next item
endpoints = endpoints[1:] + endpoints[:1]
print(len(startpoints))
print("\n".join(["%d %s" % (start+1,end+1) for (start,end) in zip(startpoints,endpoints)]))


#!/usr/bin/env python3

# Read data
n = int(input())
f = map(int, input().split())
f = [d-1 for d in f] # Make indices 0-based

# Determine in-degree of all the nodes
indegree = [0 for i in range(n)]
for i in range(n):
  indegree[f[i]] += 1

# Nodes with indegree = 0 will need to be an end-point of a new edge
endpoints = [i for i in range(n) if (indegree[i] == 0)]
nr_indegree_zero = len(endpoints)

# Determine which (hereto unvisited) nodes will be reached by these endpoints
unvisited = set(range(n))
reach = [None for i in range(n)]

def determine_reach(v):
  path = [v]
  while (v in unvisited):
    unvisited.remove(v)
    v = f[v]
    path.append(v)
  for i in path:
    reach[i] = v

for v in endpoints:
  determine_reach(v)

# The reached nodes form good start-points for the new edges
startpoints = [reach[v] for v in endpoints]

# Check for isolated cycles that are not connected to the rest of the graph
nr_cycles = 0
while len(unvisited) > 0:
  nr_cycles += 1
  v = unvisited.pop()
  determine_reach(v)
  endpoints.append(v)
  startpoints.append(reach[v])

# Special case: no indegree 0 nodes and only 1 cycle
if (nr_indegree_zero == 0) and (nr_cyles == 1):
  # No edges need to be added
  print(0)
  exit()

# Rotate the lists to each start point connects to the end-point of the next item
endpoints = endpoints[1:] + endpoints[:1]
print(len(startpoints))
print("\n".join(["%d %s" % (start+1,end+1) for (start,end) in zip(startpoints,endpoints)]))


def dfs(f, b, v)
	until b[v] do
		b[v] = 1
		v = f[v]
	end
	v
end

n = gets.to_i
f = [0] + gets.split.map(&:to_i)
a = [nil]*(n+1)
b = [nil]*(n+1)
r = []

f.each{|i| a[i] = 1}
[*1..n].each{|i| r << [i, dfs(f, b, i)] unless a[i]}
[*1..n].each{|i| r << [i, dfs(f, b, i)] unless b[i]}
r = [] if a[1..n].all? && r.size == 1
p r.size
r.each_index{|i| puts "#{r[i - 1][1]} #{r[i][0]}"}


def dfs(f, b, v)
 until b[v] do
  b[v] = 1
  v = f[v]
 end
 v
end

n = gets.to_i
f = [0] + gets.split.map(&:to_i)
a = [nil]*(n+1)
b = [nil]*(n+1)
r = []

f.each{|i| a[i] = 1}
[*1..n].each{|i| r << [i, dfs(f, b, i)] unless a[i]}
[*1..n].each{|i| r << [i, dfs(f, b, i)] unless b[i]}
r = [] if a[1..n].all? && r.size == 1
p r.size
r.each_index{|i| puts "#{r[i - 1][1]} #{r[i][0]}"}


def solve(a)
    n = a.size
    begins = (1..n).to_a - a
    begins.map! { |x| x - 1 }

    used = Array.new(n, false)
    pairs = []

    begins.each do |x|
        e = x
        while !used[e]
            used[e] = true
            e = a[e] - 1
        end

        pairs << [x, e]
    end

    used.each_with_index do |u, i|
        next if u
        e = i
        while !used[e]
            used[e] = true
            e = a[e] - 1
        end
        pairs << [i, i]
    end

    ans = []
    return ans if pairs.size == 1 && pairs[0][0] == pairs[0][1]

    pairs.flatten!
    pairs << pairs.shift

    0.step(pairs.size-1, 2) do |i|
        ans <<= [pairs[i]+1, pairs[i+1]+1]
    end

    ans
end

gets
a = gets.split(' ').map { |x| x.to_i }
#a = [2, 3, 1, 3, 4, 4, 1]
ans = solve a

puts ans.size
ans.each do |pair|
    puts pair.join(' ')
end


import sys

def get_heads(graph, num_vertices):
    return set(range(0, num_vertices)) - set(graph)

def get_components(graph, heads):
    cycle_rep2heads = {}
    head2cycle_rep = {}

    explored = [-1] * len(graph)

    def search(start, is_head):
        curr = start
        while explored[curr] == -1:
            explored[curr] = start
            curr = graph[curr]
        # already saw curr before: must be in a cycle
        prev_head = explored[curr]
        if is_head:
            if prev_head in head2cycle_rep:
                curr_cycrep = head2cycle_rep[prev_head]
                head2cycle_rep[start] = curr_cycrep
                cycle_rep2heads[curr_cycrep].append(start)
            else:
                head2cycle_rep[start] = curr
                cycle_rep2heads[curr] = [start]
        else:
            cycle_rep2heads[curr] = []

    for head in heads:
        search(head, True)
    for vert, comp in enumerate(explored):
        if comp == -1:
            search(vert, False)
    return head2cycle_rep, cycle_rep2heads

def go():
    num_vertices = int(sys.stdin.next())

    # subtract by 1
    graph = [int(num) - 1 for num in sys.stdin.next().split(' ')]
    heads = get_heads(graph, num_vertices)
    head2cycle_rep, cycle_rep2heads = get_components(graph, heads)
##### DEBUG
#    expected_num = 0
#    for cy_guy in cycle_rep2heads:
#        heads = cycle_rep2heads[cy_guy]
#
#        expected_num += len(heads) if heads else 1
#
##### END DEBUG

    new_edges = {}

    guys = cycle_rep2heads.items()
    cy_rep = guys[0][0]
    heads = guys[0][1]
    first = heads[0] if heads else cy_rep
    for idx, head in enumerate(heads[:-1]):
        new_edges[head] = heads[idx+1]
    prev_last = cy_rep

    for cy_rep, heads in guys[1:]:
        new_edges[prev_last] = heads[0] if heads else cy_rep

        for idx, head in enumerate(heads[:-1]):
            new_edges[head] = heads[idx+1]
        prev_last = cy_rep

    if prev_last != first:
        new_edges[prev_last] = first

    # print soln
###### DEBUG
    #print "EXPECTED: {}".format(expected_num)
###### END DEBUG
    print len(new_edges)
    for v1, v2 in new_edges.items():
        v1 += 1
        v2 += 1
        print v1, v2

go()


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

if len(starts) > 0:
  for i in xrange(n):
    if t[i]: dfs(i)

  ends = ends[1:] + ends[:1]
  print len(starts)
  print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(ends, starts)])
else:
  print 0


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]

ind = n*[0]
for i in g: ind[i] += 1

jnd = n*[0]
for i, v in enumerate(ind):
  if v > 0: jnd[g[i]] += 1

last = -1
for i, v in enumerate(jnd):
  if v > 1:
    if last == -1: last = i
    else:
      print "{} {}".format(last+1, i+1)
      i = last

for i, v in enumerate(ind):
  if v == 0:
    print "{} {}".format(last+1, i+1)
    i = last


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

if len(starts) > 0:
  for i in xrange(n):
    if t[i]: dfs(i)

  ends = ends[1:] + ends[:1]
  print len(starts)
  print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(ends, starts)])
else:
  for i in xrange(n):
    if t[i]: dfs(i)

  if len(starts) > 1:
    ends = ends[1:] + ends[:1]
    print len(starts)
    print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(ends, starts)])
  else:
    print 0


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

for i in xrange(n):
  if t[i]: dfs(i)

ends = ends[1:] + ends[:1]
print len(starts)
print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(ends, starts)])


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

if len(starts) == 0:
  for i in xrange(n):
    if t[i]: dfs(i)

  ends = ends[1:] + ends[:1]
  print len(starts)
  print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(ends, starts)])
else:
  print 0


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

for i in xrange(n):
  if t[i]: dfs(i)

ends = ends[1:] + ends[:1]
print len(starts)
print "\n".join(["{} {}".format(i, j) for i, j in zip(starts, ends)])


n = input()
g = [int(x) - 1 for x in raw_input().strip().split()]
t = n*[True]

ind = n*[0]
for i in g: ind[i] += 1

starts = []
ends = []

def dfs(i):
  starts.append(i)
  t[i] = False
  while t[g[i]]:
    i = g[i]
    t[i] = False
  ends.append(i)

for i, v in enumerate(ind):
  if not v: dfs(i)

for i in xrange(n):
  if t[i]: dfs(i)

ends = ends[1:] + ends[:1]
print len(starts)
print "\n".join(["{} {}".format(i+1, j+1) for i, j in zip(starts, ends)])


/* practice with Dukkha */
#include <stdio.h>

#define N	100000
#define M	100000

int ll[1 + M * 2 + M], jj[1 + M * 2 + M];

int link(int l, int j) {
	static int _ = 1;

	ll[_] = l, jj[_] = j;
	return _++;
}

int ae[N], af[N], cc[N], ae_[N], df_[N];
int pp[N], qq[N], cnt;
char visited[N];

void dfs1(int i) {
	int l;

	if (cc[i])
		return;
	cc[i] = -1;
	for (l = ae[i]; l; l = ll[l]) {
		int j = jj[l];

		dfs1(j);
	}
	qq[cnt++] = i;
}

void dfs2(int j, int c) {
	int l;

	if (cc[j] != -1) {
		int c_ = cc[j];

		if (c_ != c) {
			ae_[c_] = link(ae_[c_], c);
			df_[c]++;
		}
		return;
	}
	cc[j] = c;
	for (l = af[j]; l != 0; l = ll[l]) {
		int i = jj[l];

		dfs2(i, c);
	}
}

int dfs3(int i) {
	int l;

	if (visited[i])
		return -1;
	visited[i] = 1;
	if (ae_[i] == 0)
		return i;
	for (l = ae_[i]; l; l = ll[l]) {
		int j = jj[l];

		if ((j = dfs3(j)) != -1)
			return j;
	}
	return -1;
}

void add(int i, int j) {
	pp[cnt] = i, qq[cnt] = j, cnt++;
}

void augment(int n) {
	static int vv[N], ww[N];
	static char inv[N], inw[N];
	int p, q, s, t, h, i, x;

	p = 0;
	for (i = 0; i < n; i++)
		if (cc[i] == i && df_[i] == 0) {
			int j = dfs3(i);

			if (j != -1) {
				inv[vv[p] = i] = inw[ww[p] = j] = 1;
				p++;
			}
		}
	s = t = p;
	for (i = 0; i < n; i++)
		if (cc[i] == i) {
			if (!inv[i] && df_[i] == 0)
				vv[s++] = i;
			if (!inw[i] && ae_[i] == 0)
				ww[t++] = i;
		}
	q = s < t ? s : t;
	cnt = 0;
	for (h = 0; h < p - 1; h++)
		add(ww[h], vv[h + 1]);
	x = ww[p - 1];
	for (h = q; h < s; h++)
		add(x, vv[h]), x = vv[h];
	for (h = q; h < t; h++)
		add(x, ww[h]), x = ww[h];
	add(x, vv[0]);
	for (h = p; h < q; h++)
		add(ww[h], vv[h]);
}

int main() {
	int n, h, i, j, k;

	scanf("%d", &n);
	for (i = 0; i < n; i++) {
		scanf("%d", &j), j--;
		ae[i] = link(ae[i], j);
		af[j] = link(af[j], i);
	}
	for (i = 0; i < n; i++)
		if (!cc[i])
			dfs1(i);
	k = 0;
	for (h = n - 1; h >= 0; h--) {
		j = qq[h];
		if (cc[j] == -1) {
			k++;
			dfs2(j, j);
		}
	}
	if (k == 1) {
		printf("0\n");
		return 0;
	}
	augment(n);
	printf("%d\n", cnt);
	while (cnt--)
		printf("%d %d\n", pp[cnt] + 1, qq[cnt] + 1);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int*bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=s;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+1, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(!in_deg[i]){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	if(ptr==1 && src[0]==bin[0]) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int *bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=tr->val;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+2, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(in_deg[i]==0){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			src[ptr]=1;
			dfs(g, i, vis, bin);
		}
	}
	if(ptr==0 || (ptr==1 && src[0])==bin[0]) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int src, bool *vis, int *topo){
	vis[src]=1;
	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, topo);
		tr = tr->next;
	}
	topo[ptr++]=src;
}

void condense(graph *g, int src, bool *vis, int *belongs_to, int par){
	vis[src]=1;
	belongs_to[src]=par;

	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) condense(g, tr->val, vis, belongs_to, par);
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	graph *rg = (graph*)malloc(sizeof(graph));

	__init(g, n+1);
	__init(rg, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);

		add_edge(g, i, x);
		add_edge(rg, x, i);
	}

	int *topo = (int*)malloc((n+1)*sizeof(int));
	ptr=0;
	bool *vis = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			dfs(g, i, vis, topo);
		}
	}

	int *belongs_to = (int*)malloc((n+1)*sizeof(int));
	for(int i=0; i<=n; i++) vis[i]=0;

	for(int i=0; i<n; i++){
		if(!vis[topo[n-1-i]]){
			condense(rg, topo[n-1-i], vis, belongs_to, topo[n-1-i]);
		}
	}

	// for(int i=1; i<=n; i++) printf("%d ", belongs_to[i]);
	// printf("\n");

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	int p1=0, p2=0;

	bool *in_deg = (bool*)calloc(n+1, sizeof(bool));
	bool *out_deg = (bool*)calloc(n+1, sizeof(bool));

	 for(int i=1; i<=n; i++) vis[i]=0;

	for(int i=1; i<=n; i++){
		vis[belongs_to[i]]=1;
		node* tr = g->adj[i].head;
		while(tr){
			if(belongs_to[i]!=belongs_to[tr->val]){
				out_deg[belongs_to[i]]=1;
				in_deg[belongs_to[tr->val]]=1;
			}
			tr = tr->next;
		}
	}

	// for(int i=1; i<=n; i++){
	// 	printf("%d %d\n", in_deg[i], out_deg[i]);
	// }
	int scc=0;
	for(int i=1; i<=n; i++){
		if(!vis[i]) continue;
		if(in_deg[i] && !out_deg[i]) bin[p2++]=i;
		else if(!in_deg[i] && out_deg[i]) src[p1++]=i;
		else if(!in_deg[i] && !out_deg[i]){
			scc++;
			bin[p2++]=i;
			src[p1++]=i;
		}
	}

	if(p1==0 || (p1==1 && scc==1))  printf("%d\n", 0);
	else{
		printf("%d\n", p2+p1-1);
		for(int i=1; i<p1; i++){
			printf("%d %d\n", src[0], src[i]);
		}
		for(int i=0; i<p2; i++){
			printf("%d %d\n", bin[i], src[0]);
		}
	}
	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int src, bool *vis, int *topo){
	vis[src]=1;
	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, topo);
		tr = tr->next;
	}
	topo[ptr++]=src;
}

void condense(graph *g, int src, bool *vis, int *belongs_to, int par){
	vis[src]=1;
	belongs_to[src]=par;

	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) condense(g, tr->val, vis, belongs_to, par);
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	graph *rg = (graph*)malloc(sizeof(graph));

	__init(g, n+1);
	__init(rg, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);

		add_edge(g, i, x);
		add_edge(rg, x, i);
	}

	int *topo = (int*)malloc((n+1)*sizeof(int));
	ptr=0;
	bool *vis = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			dfs(g, i, vis, topo);
		}
	}

	int *belongs_to = (int*)malloc((n+1)*sizeof(int));
	for(int i=0; i<=n; i++) vis[i]=0;

	for(int i=0; i<n; i++){
		if(!vis[topo[n-1-i]]){
			condense(rg, topo[n-1-i], vis, belongs_to, topo[n-1-i]);
		}
	}

	// for(int i=1; i<=n; i++) printf("%d ", belongs_to[i]);
	// printf("\n");

	int *src = (int*)malloc(sizeof(int));
	int *bin = (int*)malloc(sizeof(int));

	int p1=0, p2=0;

	bool *in_deg = (bool*)calloc(n+1, sizeof(bool));
	bool *out_deg = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		node* tr = g->adj[i].head;
		while(tr){
			if(belongs_to[i]!=belongs_to[tr->val]){
				out_deg[belongs_to[i]]=1;
				in_deg[belongs_to[tr->val]]=1;
			}
			tr = tr->next;
		}
	}

	// for(int i=1; i<=n; i++){
	// 	printf("%d %d\n", in_deg[i], out_deg[i]);
	// }

	for(int i=1; i<=n; i++){
		if(in_deg[i] && !out_deg[i]) bin[p2++]=i;
		if(!in_deg[i] && out_deg[i]) src[p1++]=i;
	}

	if(p1==0) printf("%d\n", 0);
	else{
		printf("%d\n", p2+p1-1);
		for(int i=1; i<p1; i++){
			printf("%d %d\n", src[0], src[i]);
		}
		for(int i=0; i<p2; i++){
			printf("%d %d\n", bin[i], src[0]);
		}
	}
	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int src, bool *vis, int *topo){
	vis[src]=1;
	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, topo);
		tr = tr->next;
	}
	topo[ptr++]=src;
}

void condense(graph *g, int src, bool *vis, int *belongs_to, int par){
	vis[src]=1;
	belongs_to[src]=par;

	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) condense(g, tr->val, vis, belongs_to, par);
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	graph *rg = (graph*)malloc(sizeof(graph));

	__init(g, n+1);
	__init(rg, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);

		add_edge(g, i, x);
		add_edge(rg, x, i);
	}

	int *topo = (int*)malloc((n+1)*sizeof(int));
	ptr=0;
	bool *vis = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			dfs(g, i, vis, topo);
		}
	}

	int *belongs_to = (int*)malloc((n+1)*sizeof(int));
	for(int i=0; i<=n; i++) vis[i]=0;

	for(int i=0; i<n; i++){
		if(!vis[topo[n-1-i]]){
			condense(rg, topo[n-1-i], vis, belongs_to, topo[n-1-i]);
		}
	}

	// for(int i=1; i<=n; i++) printf("%d ", belongs_to[i]);
	// printf("\n");

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	int p1=0, p2=0;

	bool *in_deg = (bool*)calloc(n+1, sizeof(bool));
	bool *out_deg = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		node* tr = g->adj[i].head;
		while(tr){
			if(belongs_to[i]!=belongs_to[tr->val]){
				out_deg[belongs_to[i]]=1;
				in_deg[belongs_to[tr->val]]=1;
			}
			tr = tr->next;
		}
	}

	// for(int i=1; i<=n; i++){
	// 	printf("%d %d\n", in_deg[i], out_deg[i]);
	// }

	for(int i=1; i<=n; i++){
		if(in_deg[i] && !out_deg[i]) bin[p2++]=i;
		if(!in_deg[i] && out_deg[i]) src[p1++]=i;
	}

	if(p1==0) printf("%d\n", 0);
	else{
		printf("%d\n", p2+p1-1);
		for(int i=1; i<p1; i++){
			printf("%d %d\n", src[0], src[i]);
		}
		for(int i=0; i<p2; i++){
			printf("%d %d\n", bin[i], src[0]);
		}
	}
	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int*bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=s;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+1, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(!in_deg[i]){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	if(ptr==0) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int *bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=tr->val;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+2, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(in_deg[i]==0){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	if(ptr==0) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int *bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=tr->val;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+2, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(in_deg[i]==0){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}
	if(ptr==0 || (ptr==1 && src[0])==bin[0]) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int src, bool *vis, int *topo){
	vis[src]=1;
	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, topo);
		tr = tr->next;
	}
	topo[ptr++]=src;
}

void condense(graph *g, int src, bool *vis, int *belongs_to, int par){
	vis[src]=1;
	belongs_to[src]=par;

	node* tr =  g->adj[src].head;
	while(tr){
		if(!vis[tr->val]) condense(g, tr->val, vis, belongs_to, par);
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	graph *rg = (graph*)malloc(sizeof(graph));

	__init(g, n+1);
	__init(rg, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);

		add_edge(g, i, x);
		add_edge(rg, x, i);
	}

	int *topo = (int*)malloc((n+1)*sizeof(int));
	ptr=0;
	bool *vis = (bool*)calloc(n+1, sizeof(bool));

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			dfs(g, i, vis, topo);
		}
	}

	int *belongs_to = (int*)malloc((n+1)*sizeof(int));
	for(int i=0; i<=n; i++) vis[i]=0;

	for(int i=0; i<n; i++){
		if(!vis[topo[n-1-i]]){
			condense(rg, topo[n-1-i], vis, belongs_to, topo[n-1-i]);
		}
	}

	// for(int i=1; i<=n; i++) printf("%d ", belongs_to[i]);
	// printf("\n");

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	int p1=0, p2=0;

	bool *in_deg = (bool*)calloc(n+1, sizeof(bool));
	bool *out_deg = (bool*)calloc(n+1, sizeof(bool));

	 for(int i=1; i<=n; i++) vis[i]=0;

	for(int i=1; i<=n; i++){
		vis[belongs_to[i]]=1;
		node* tr = g->adj[i].head;
		while(tr){
			if(belongs_to[i]!=belongs_to[tr->val]){
				out_deg[belongs_to[i]]=1;
				in_deg[belongs_to[tr->val]]=1;
			}
			tr = tr->next;
		}
	}

	// for(int i=1; i<=n; i++){
	// 	printf("%d %d\n", in_deg[i], out_deg[i]);
	// }

	for(int i=1; i<=n; i++){
		if(!vis[i]) continue;
		if(in_deg[i] && !out_deg[i]) bin[p2++]=i;
		else if(!in_deg[i] && out_deg[i]) src[p1++]=i;
		else if(!in_deg[i] && !out_deg[i]){
			bin[p2++]=i;
			src[p1++]=i;
		}
	}

	if(p1==0) printf("%d\n", 0);
	else{
		printf("%d\n", p2+p1-1);
		for(int i=1; i<p1; i++){
			printf("%d %d\n", src[0], src[i]);
		}
		for(int i=0; i<p2; i++){
			printf("%d %d\n", bin[i], src[0]);
		}
	}
	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include <time.h>

typedef struct node{
	int val;
	struct node *next;
}node;

typedef struct list{
	node* head;
}list;

typedef struct graph{
	list* adj;
}graph;

void __init(graph *g, int n){
	g->adj = (list*)malloc(n*sizeof(list));

	for(int i=0; i<n; i++) {
		g->adj[i].head = NULL;
	}
}

void add_edge(graph *g, int src, int dest){
	node *new_node = (node*)malloc(sizeof(node));
	new_node->val = dest;
	new_node->next = g->adj[src].head;
	g->adj[src].head = new_node;
}

int ptr;

void dfs(graph *g, int s, bool *vis, int *bin){
	vis[s]=1;
	node* tr =  g->adj[s].head;
	while(tr){
		if(!vis[tr->val]) dfs(g, tr->val, vis, bin);
		else{
			bin[ptr++]=tr->val;
			return ;
		}
		tr = tr->next;
	}
}

int main(){
	#ifndef ONLINE_JUDGE
    freopen("input.txt", "r" , stdin);
    freopen("output.txt", "w" , stdout);
    #endif

    clock_t start_t, end_t;
    start_t = clock();

	int n;
	scanf("%d", &n);
	graph *g = (graph*)malloc(sizeof(graph));
	int *in_deg = (int*)calloc(n+2, sizeof(int));

	__init(g, n+1);

	int x;
	for(int i=1; i<=n; i++){
		scanf("%d", &x);
		in_deg[x]=1;
		add_edge(g, i, x);
	}

	int *src = (int*)malloc(n*sizeof(int));
	int *bin = (int*)malloc(n*sizeof(int));

	bool *vis = (bool*)calloc(n+1, sizeof(bool));
	ptr=0;

	for(int i=1; i<=n; i++){
		if(in_deg[i]==0){
			src[ptr]=i;
			dfs(g, i, vis, bin);
		}
	}

	for(int i=1; i<=n; i++){
		if(!vis[i]){
			src[ptr]=1;
			dfs(g, i, vis, bin);
		}
	}
	if(ptr==0) printf("%d\n", 0);
	else{
		printf("%d\n", ptr);
		for(int i=0; i<ptr-1; i++) printf("%d %d\n", bin[i], src[i+1]);
		printf("%d %d\n", bin[ptr-1],src[0]);
	}

	end_t = clock();
	fprintf(stderr, "Time : %lf ms\n", 1000*(double)(end_t - start_t) / CLOCKS_PER_SEC);
	return 0;
}


#include<stdio.h>
#define T 100000

int g[T], in_circ[T], v[T], is_in[T];
int r[T][2], ans;
int fun(int i);

int main()
{
    int n, i;
    scanf("%d", &n);
    for(i = 0; i < n; ++i){
        scanf("%d", &g[i]);
        --g[i];
        is_in[g[i]] = 1;
    }
    int c1 = 0;
    for(i = 0; i < n; ++i){
        if(!is_in[i]){
            r[ans][0] = i + 1;
            r[ans][1] = fun(i) + 1;
            ++ans;
            ++c1;
        }
    }
    int c2 = 0;
    for(i = 0; i < n; ++i){
        if(!v[i]){
            r[ans][0] = i + 1;
            r[ans][1] = i + 1;
            ++ans;
            ++c2;
            fun(i);
        }
    }
    if(c2 == 1 && c1 == 0){
        printf("0");
        return 0;
    }
    printf("%d\n", ans);
    if(ans > 0){
        for(i = 1; i < ans; ++i){
            printf("%d %d\n", r[i - 1][1], r[i][0]);
        }
        printf("%d %d", r[ans - 1][1], r[0][0]);
    }
    return 0;
}

int fun(int i)
{
    v[i] = 1;
    int anss;
    if(v[g[i]] == 1){
        in_circ[i] = g[i];
        anss = in_circ[i];
    } else if(v[g[i]] == 0){
        in_circ[i] = fun(g[i]);
        anss = in_circ[i];
    } else anss = in_circ[i] = in_circ[g[i]];
    v[i] = 2;
    return anss;
}


#include <stdio.h>
#include<stdio.h>
#define T 100000

int main() {
    int i,n,g[T];
    scanf("%d", &n);
    for(i = 0; i < n; ++i){
        scanf("%d", &g[i]);
    }
    printf("%d\n",1);
    printf("%d %d",2,1);
    return 0;
}


using System;
using System.Linq;
using System.Collections.Generic;

class Program {
    public struct Tuple<U, V> {
        public U Item1; public V Item2;
        public Tuple(U x, V y) { Item1 = x; Item2 = y;}
    }
    public static Tuple<U, V> MakeTuple<U, V>(U x, V y) { return new Tuple<U, V>(x, y); }

    private static int[] a;
    private static int n;
    public static void Main(string[] args) {
        n = int.Parse(Console.ReadLine());
        a = Console.ReadLine().Split(' ').Select(x => int.Parse(x) - 1).ToArray();
        var inc = new int[n];
        foreach (int v in a)
            ++inc[v];

        used = new bool[n];
        col = new int[n];
        var comp = new List<Tuple<int, int>>();
        for (int i = 0; i < n; ++i)
            if (inc[i] == 0) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, col[i] + 1));
            }
        for (int i = 0; i < n; ++i)
            if (!used[i]) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, i + 1));
            }

        if (comp.Count == 1 && comp[0].Item1 == comp[0].Item2)
            Console.WriteLine(0);
        else {
            Console.WriteLine(comp.Count);
            for (int i = 0; i < comp.Count; ++i)
                Console.WriteLine("{0} {1}", comp[i].Item2, comp[(i + 1) % comp.Count].Item1);
        }
    }

    private static bool[] used;
    private static int[] col;
    private static void dfs(int v) {
        var st = new Stack<int>();
        for(; !used[v]; v = a[v]) {
            st.Push(v);
            used[v] = true;
        }
        while (st.Count > 0) {
            v = st.Pop();
            col[v] = col[a[v]];
            if (col[v] == 0) col[v] = v;
        }
    }
}


using System;
using System.Linq;
using System.Collections.Generic;

class Program {
    public static Tuple<U, V> MakeTuple<U, V>(U x, V y) { return new Tuple<U, V>(x, y); }

    private static int[] a;
    private static int n;
    public static void Main(string[] args) {
        n = int.Parse(Console.ReadLine());
        a = Console.ReadLine().Split(' ').Select(x => int.Parse(x) - 1).ToArray();
        var inc = new int[n];
        foreach (int v in a)
            ++inc[v];

        used = new bool[n];
        col = new int[n];
        var comp = new List<Tuple<int, int>>();
        for (int i = 0; i < n; ++i)
            if (inc[i] == 0) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, col[i] + 1));
            }
        for (int i = 0; i < n; ++i)
            if (!used[i]) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, i + 1));
            }

        if (comp.Count == 1 && comp[0].Item1 == comp[0].Item2)
            Console.WriteLine(0);
        else {
            Console.WriteLine(comp.Count);
            for (int i = 0; i < comp.Count; ++i)
                Console.WriteLine("{0} {1}", comp[i].Item2, comp[(i + 1) % comp.Count].Item1);
        }
    }

    private static bool[] used;
    private static int[] col;
    private static void dfs(int v) {
        used[v] = true;
        if (!used[a[v]]) dfs(a[v]);
        col[v] = col[a[v]];
        if (col[v] == 0) col[v] = v;
    }
}


using System;
using System.Linq;
using System.Collections.Generic;

class Program {
    public struct Tuple<U, V> {
        public U Item1; public V Item2;
        public Tuple(U x, V y) { Item1 = x; Item2 = y;}
    }
    public static Tuple<U, V> MakeTuple<U, V>(U x, V y) { return new Tuple<U, V>(x, y); }

    private static int[] a;
    private static int n;
    public static void Main(string[] args) {
        n = int.Parse(Console.ReadLine());
        a = Console.ReadLine().Split(' ').Select(x => int.Parse(x) - 1).ToArray();
        var inc = new int[n];
        foreach (int v in a)
            ++inc[v];

        used = new bool[n];
        col = new int[n];
        var comp = new List<Tuple<int, int>>();
        for (int i = 0; i < n; ++i)
            if (inc[i] == 0) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, col[i] + 1));
            }
        for (int i = 0; i < n; ++i)
            if (!used[i]) {
                dfs(i);
                comp.Add(MakeTuple(i + 1, i + 1));
            }

        if (comp.Count == 1 && comp[0].Item1 == comp[0].Item2)
            Console.WriteLine(0);
        else {
            Console.WriteLine(comp.Count);
            for (int i = 0; i < comp.Count; ++i)
                Console.WriteLine("{0} {1}", comp[i].Item2, comp[(i + 1) % comp.Count].Item1);
        }
    }

    private static bool[] used;
    private static int[] col;
    private static void dfs(int v) {
        used[v] = true;
        if (!used[a[v]]) dfs(a[v]);
        col[v] = col[a[v]];
        if (col[v] == 0) col[v] = v;
    }
}


//Template by _Trung_Nguyen
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using System.IO;

using Libraries;
using Libraries.IO;
using Libraries.Collections;

namespace Codeforces
{
	public class sample
	{
		private static int solution()
		{
			int n = cin.ReadInt32();

			int [] f = cin.ReadInt32Array();
			for (int i = 0; i < n; ++i) --f[i];

			int [] from = Arrays.Fill(n, -1).ToArray();

			for (int i = 0; i < n; ++i) from[f[i]] = i;

			bool [] visited = new bool[n];

			List<int> start = new List<int>();
			List<int> end = new List<int>();

			for (int i = 0; i < n; ++i)
			{
				if (from[i] != -1) continue;
				if (visited[i]) continue;

				start.Add(i);

				int current = i;
				while (!visited[f[current]])
				{
					visited[current] = true;
					current = f[current];
				}

				visited[current] = true;

				end.Add(current);
			}

			if (start.Count > 0)
			for (int i = 0; i < n; ++i)
			{
				if (visited[i]) continue;

				start.Add(i);
				end.Add(i);

				int current = i;
				while (!visited[f[current]])
				{
					visited[current] = true;
					current = f[current];
				}

				visited[current] = true;
			}

			int k = start.Count;
			cout.WriteLine(k);

			for (int i = 0; i < k; ++i)
			{
				cout.WriteLine("{0} {1}", end[i] + 1, start[(i + 1) % k] + 1);
			}

			return 0;
		}

//===============Prewritten code======================
		private static istream cin = new istream(Console.OpenStandardInput());
		private static ostream cout = new ostream(Console.OpenStandardOutput());
		private static ostream cerr = new ostream(Console.OpenStandardError());
		private static ostream clog = new ostream(Console.OpenStandardError(), true);

		public static int Main()
		{
			try
			{
				int returnValue = solution();
				cout.Flush();
				cerr.Flush();
				return returnValue;
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
				return -1;
			}
		}
	}
}

namespace Libraries.IO
{
	public class istream : IDisposable
	{
		private BufferedStream buffer;
		private StreamReader reader;
		private Queue<string> words = new Queue<string>();
		private void getWords()
		{
			while (words.Count == 0)
			{
				string [] temp = reader.ReadLine().Split();
				foreach (var str in temp)
				{
					if (str.Length == 0) continue;
					words.Enqueue(str);
				}
			}
		}
		public istream(Stream istr)
		{
			buffer = new BufferedStream(istr);
			reader = new StreamReader(buffer);
		}
		public int Peek()
		{
			return reader.Peek();
		}
		public int Read()
		{
			return reader.Read();
		}
		public string ReadLine()
		{
			return reader.ReadLine();
		}
		public int [] ReadInt32Array()
		{
			return reader.ReadLine().Split().Select(x => Convert.ToInt32(x)).ToArray();
		}
		public long [] ReadInt64Array()
		{
			return reader.ReadLine().Split().Select(x => Convert.ToInt64(x)).ToArray();
		}
		public string [] ReadStringArray()
		{
			return reader.ReadLine().Split();
		}
		public int ReadInt32()
		{
			getWords();
			return Convert.ToInt32(words.Dequeue());
		}
		public long ReadInt64()
		{
			getWords();
			return Convert.ToInt64(words.Dequeue());
		}
		public string ReadString()
		{
			getWords();
			return words.Dequeue();
		}
		public void Dispose()
		{
			reader.Dispose();
			buffer.Dispose();
		}
	}
	public class ostream : IDisposable
	{
		private BufferedStream buffer;
		private StreamWriter writer;
		public ostream(Stream ostr, bool autoflush = false)
		{
			buffer = new BufferedStream(ostr);
			writer = new StreamWriter(buffer);
			writer.AutoFlush = autoflush;
		}
		public void Write(string format, params object[] args)
		{
			writer.Write(format, args);
		}
		public void Write<T>(T value)
		{
			writer.Write(value);
		}
		public void Write<T>(T [] arr, string sep)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, x);
		}
		public void WriteLine(string format, params object[] args)
		{
			writer.WriteLine(format, args);
		}
		public void WriteLine<T>(T value)
		{
			writer.WriteLine(value);
		}
		public void WriteLine<T>(IEnumerable<T> arr, string sep)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, x);
			writer.WriteLine();
		}
		public void WriteLine<T>(IEnumerable<T> arr, string sep, Func<T, T> funct)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, funct(x));
			writer.WriteLine();
		}
		public void WriteLine()
		{
			writer.WriteLine();
		}
		public void Flush()
		{
			writer.Flush();
		}
		public void Dispose()
		{
			writer.Dispose();
			buffer.Dispose();
		}
	}
}
namespace Libraries
{
	public class Utilities
	{
		public static void Swap<T>(ref T x, ref T y)
		{
			T temp = y;
			y = x;
			x = temp;
		}
	}

	public static class Arrays
	{
		private static T Inc<T>(ref T value)
		{
			dynamic temp = value;
			dynamic temp2 = temp;
			value = ++temp2;
			return temp++;
		}
		public static IEnumerable<T> FillDefault<T>(int size) where T : new()
		{
			for (int i = 0; i < size; ++i) yield return new T();
		}
		public static IEnumerable<T> Fill<T>(int size, T value)
		{
			for (int i = 0; i < size; ++i) yield return value;
		}
		public static IEnumerable<T> Iota<T>(int size, T init)
		{
			for (int i = 0; i < size; ++i) yield return Inc(ref init);
		}
		public static T First<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			return container[0];
		}
		public static T Last<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			return container[container.Count - 1];
		}
		public static void RemoveLast<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			container.RemoveAt(container.Count - 1);
		}
	}
}

namespace Libraries.Collections
{
	class PriorityQueue<T> where T : IComparable
	{
	    private List<T> list;
	    public int Count { get { return list.Count; } }
	    public readonly bool IsDescending;

	    public PriorityQueue()
	    {
	        list = new List<T>();
	    }

	    public PriorityQueue(bool isdesc)
	        : this()
	    {
	        IsDescending = isdesc;
	    }

	    public PriorityQueue(int capacity)
	        : this(capacity, false)
	    { }

	    public PriorityQueue(IEnumerable<T> collection)
	        : this(collection, false)
	    { }

	    public PriorityQueue(int capacity, bool isdesc)
	    {
	        list = new List<T>(capacity);
	        IsDescending = isdesc;
	    }

	    public PriorityQueue(IEnumerable<T> collection, bool isdesc)
	        : this()
	    {
	        IsDescending = isdesc;
	        foreach (var item in collection)
	            Enqueue(item);
	    }

	    public void Enqueue(T x)
	    {
	        list.Add(x);
	        int i = Count - 1;

	        while (i > 0)
	        {
	            int p = (i - 1) / 2;
	            if ((IsDescending ? -1 : 1) * list[p].CompareTo(x) <= 0) break;

	            list[i] = list[p];
	            i = p;
	        }

	        if (Count > 0) list[i] = x;
	    }

	    public T Dequeue()
	    {
	        T target = Peek();
	        T root = list[Count - 1];
	        list.RemoveAt(Count - 1);

	        int i = 0;
	        while (i * 2 + 1 < Count)
	        {
	            int a = i * 2 + 1;
	            int b = i * 2 + 2;
	            int c = b < Count && (IsDescending ? -1 : 1) * list[b].CompareTo(list[a]) < 0 ? b : a;

	            if ((IsDescending ? -1 : 1) * list[c].CompareTo(root) >= 0) break;
	            list[i] = list[c];
	            i = c;
	        }

	        if (Count > 0) list[i] = root;
	        return target;
	    }

	    public T Peek()
	    {
	        if (Count == 0) throw new InvalidOperationException("Queue is empty.");
	        return list[0];
	    }

	    public void Clear()
	    {
	        list.Clear();
	    }
	}
    internal class DequeUtility
    {
        public static int ClosestPowerOfTwoGreaterThan(int x)
        {
            x--;
            x |= (x >> 1);
            x |= (x >> 2);
            x |= (x >> 4);
            x |= (x >> 8);
            x |= (x >> 16);
            return (x+1);
        }

        public static int Count<TSource>(IEnumerable<TSource> source)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            ICollection<TSource> genericCollection = source as ICollection<TSource>;
            if (genericCollection != null)
            {
                return genericCollection.Count;
            }

            ICollection nonGenericCollection = source as ICollection;
            if (nonGenericCollection != null)
            {
                return nonGenericCollection.Count;
            }

            checked
            {
                int count = 0;
                using (var iterator = source.GetEnumerator())
                {
                    while (iterator.MoveNext())
                    {
                        count++;
                    }
                }
                return count;
            }
        }
    }

    public class Deque<T> : IList<T>
    {
        private const int defaultCapacity = 16;

        private int startOffset;

        private T[] buffer;

        public Deque() : this(defaultCapacity) { }

        public Deque(int capacity)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "capacity", "capacity is less than 0.");
            }

            this.Capacity = capacity;
        }

        public Deque(IEnumerable<T> collection)
            : this(DequeUtility.Count(collection))
        {
            InsertRange(0, collection);
        }

        private int capacityClosestPowerOfTwoMinusOne;

        public int Capacity
        {
            get
            {
                return buffer.Length;
            }

            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException(
                        "value",
                        "Capacity is less than 0.");
                }
                else if (value < this.Count)
                {
                    throw new InvalidOperationException(
                        "Capacity cannot be set to a value less than Count");
                }
                else if (null != buffer && value == buffer.Length)
                {
                    return;
                }

                int powOfTwo = DequeUtility.ClosestPowerOfTwoGreaterThan(value);

                value = powOfTwo;

                T[] newBuffer = new T[value];
                this.CopyTo(newBuffer, 0);

                buffer = newBuffer;
                startOffset = 0;
                this.capacityClosestPowerOfTwoMinusOne = powOfTwo - 1;
            }
        }

        public bool IsFull
        {
            get { return this.Count == this.Capacity; }
        }

        public bool IsEmpty
        {
            get { return 0 == this.Count; }
        }

        private void ensureCapacityFor(int numElements)
        {
            if (this.Count + numElements > this.Capacity)
            {
                this.Capacity = this.Count + numElements;
            }
        }

        private int toBufferIndex(int index)
        {
            int bufferIndex;

            bufferIndex = (index + this.startOffset)
                & this.capacityClosestPowerOfTwoMinusOne;

            return bufferIndex;
        }

        private void checkIndexOutOfRange(int index)
        {
            if (index >= this.Count)
            {
                throw new IndexOutOfRangeException(
                    "The supplied index is greater than the Count");
            }
        }

        private static void checkArgumentsOutOfRange(
            int length,
            int offset,
            int count)
        {
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "offset", "Invalid offset " + offset);
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "count", "Invalid count " + count);
            }

            if (length - offset < count)
            {
                throw new ArgumentException(
                    String.Format(
                    "Invalid offset ({0}) or count + ({1}) "
                    + "for source length {2}",
                    offset, count, length));
            }
        }

        private int shiftStartOffset(int value)
        {
            this.startOffset = toBufferIndex(value);

            return this.startOffset;
        }

        private int preShiftStartOffset(int value)
        {
            int offset = this.startOffset;
            this.shiftStartOffset(value);
            return offset;
        }

        private int postShiftStartOffset(int value)
        {
            return shiftStartOffset(value);
        }

        #region IEnumerable

        public IEnumerator<T> GetEnumerator()
        {
            if (this.startOffset + this.Count > this.Capacity)
            {
                for (int i = this.startOffset; i < this.Capacity; i++)
                {
                    yield return buffer[i];
                }

                int endIndex = toBufferIndex(this.Count);
                for (int i = 0; i < endIndex; i++)
                {
                    yield return buffer[i];
                }
            }
            else
            {
                int endIndex = this.startOffset + this.Count;
                for (int i = this.startOffset; i < endIndex; i++)
                {
                    yield return buffer[i];
                }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion

        #region ICollection

        bool ICollection<T>.IsReadOnly
        {
            get { return false; }
        }

        public int Count
        {
            get;
            private set;
        }

        private void incrementCount(int value)
        {
            this.Count = this.Count + value;
        }

        private void decrementCount(int value)
        {
            this.Count = Math.Max(this.Count - value, 0);
        }

        public void Add(T item)
        {
            AddLast(item);
        }

        private void ClearBuffer(int logicalIndex, int length)
        {
            int offset = toBufferIndex(logicalIndex);
            if (offset + length > this.Capacity)
            {
                int len = this.Capacity - offset;
                Array.Clear(this.buffer, offset, len);

                len = toBufferIndex(logicalIndex + length);
                Array.Clear(this.buffer, 0, len);
            }
            else
            {
                Array.Clear(this.buffer, offset, length);
            }
        }

        public void Clear()
        {
            if (this.Count > 0)
            {
                ClearBuffer(0, this.Count);
            }
            this.Count = 0;
            this.startOffset = 0;
        }

        public bool Contains(T item)
        {
            return this.IndexOf(item) != -1;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            if (null == array)
            {
                throw new ArgumentNullException("array", "Array is null");
            }

            if (null == this.buffer)
            {
                return;
            }

            checkArgumentsOutOfRange(array.Length, arrayIndex, this.Count);

            if (0 != this.startOffset
                && this.startOffset + this.Count >= this.Capacity)
            {
                int lengthFromStart = this.Capacity - this.startOffset;
                int lengthFromEnd = this.Count - lengthFromStart;

                Array.Copy(
                    buffer, this.startOffset, array, 0, lengthFromStart);

                Array.Copy(
                    buffer, 0, array, lengthFromStart, lengthFromEnd);
            }
            else
            {
                Array.Copy(
                    buffer, this.startOffset, array, 0, Count);
            }
        }

        public bool Remove(T item)
        {
            bool result = true;
            int index = IndexOf(item);

            if (-1 == index)
            {
                result = false;
            }
            else
            {
                RemoveAt(index);
            }

            return result;
        }

        #endregion

        #region List<T>

        public T this[int index]
        {
            get
            {
                return this.Get(index);
            }

            set
            {
                this.Set(index, value);
            }
        }

        public void Insert(int index, T item)
        {
            ensureCapacityFor(1);

            if (index == 0)
            {
                AddFirst(item);
                return;
            }
            else if (index == Count)
            {
                AddLast(item);
                return;
            }

            InsertRange(index, new[] { item });
        }

        public int IndexOf(T item)
        {
            int index = 0;
            foreach (var myItem in this)
            {
                if (myItem.Equals(item))
                {
                    break;
                }
                ++index;
            }

            if (index == this.Count)
            {
                index = -1;
            }

            return index;
        }

        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                RemoveFirst();
                return;
            }
            else if (index == Count - 1)
            {
                RemoveLast();
                return;
            }

            RemoveRange(index, 1);
        }
        #endregion

        public void AddFirst(T item)
        {
            ensureCapacityFor(1);
            buffer[postShiftStartOffset(-1)] = item;
            incrementCount(1);
        }

        public void AddLast(T item)
        {
            ensureCapacityFor(1);
            buffer[toBufferIndex(this.Count)] = item;
            incrementCount(1);
        }

        public T RemoveFirst()
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }

            T result = buffer[this.startOffset];
            buffer[preShiftStartOffset(1)] = default(T);
            decrementCount(1);
            return result;
        }

        public T RemoveLast()
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }

            decrementCount(1);
            int endIndex = toBufferIndex(this.Count);
            T result = buffer[endIndex];
            buffer[endIndex] = default(T);

            return result;
        }

        public void AddRange(IEnumerable<T> collection)
        {
            AddLastRange(collection);
        }

        public void AddFirstRange(IEnumerable<T> collection)
        {
            AddFirstRange(collection, 0, DequeUtility.Count(collection));
        }

        public void AddFirstRange(
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            InsertRange(0, collection, fromIndex, count);
        }

        public void AddLastRange(IEnumerable<T> collection)
        {
            AddLastRange(collection, 0, DequeUtility.Count(collection));
        }

        public void AddLastRange(
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            InsertRange(this.Count, collection, fromIndex, count);
        }

        public void InsertRange(int index, IEnumerable<T> collection)
        {
            int count = DequeUtility.Count(collection);
            this.InsertRange(index, collection, 0, count);
        }

        public void InsertRange(
            int index,
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            checkIndexOutOfRange(index - 1);

            if (0 == count)
            {
                return;
            }

            ensureCapacityFor(count);

            if (index < this.Count / 2)
            {
                if (index > 0)
                {
                    int copyCount = index;
                    int shiftIndex = this.Capacity - count;
                    for (int j = 0; j < copyCount; j++)
                    {
                        buffer[toBufferIndex(shiftIndex + j)] =
                            buffer[toBufferIndex(j)];
                    }
                }

                this.shiftStartOffset(-count);

            }
            else
            {
                if (index < this.Count)
                {
                    int copyCount = this.Count - index;
                    int shiftIndex = index + count;
                    for (int j = 0; j < copyCount; j++)
                    {
                        buffer[toBufferIndex(shiftIndex + j)] =
                            buffer[toBufferIndex(index + j)];
                    }
                }
            }

            int i = index;
            foreach (T item in collection)
            {
                buffer[toBufferIndex(i)] = item;
                ++i;
            }

            incrementCount(count);
        }

        public void RemoveRange(int index, int count)
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }
            if (index > Count - count)
            {
                throw new IndexOutOfRangeException(
                    "The supplied index is greater than the Count");
            }

            ClearBuffer(index, count);

            if (index == 0)
            {
                this.shiftStartOffset(count);
                this.Count -= count;
                return;
            }
            else if (index == Count - count)
            {
                this.Count -= count;
                return;
            }

            if ((index + (count / 2)) < Count / 2)
            {
                int copyCount = index;
                int writeIndex = count;
                for (int j = 0; j < copyCount; j++)
                {
                    buffer[toBufferIndex(writeIndex + j)]
                        = buffer[toBufferIndex(j)];
                }

                this.shiftStartOffset(count);
            }
            else
            {
                int copyCount = Count - count - index;
                int readIndex = index + count;
                for (int j = 0; j < copyCount; ++j)
                {
                    buffer[toBufferIndex(index + j)] =
                        buffer[toBufferIndex(readIndex + j)];
                }
            }

            decrementCount(count);
        }
        public T Get(int index)
        {
            checkIndexOutOfRange(index);
            return buffer[toBufferIndex(index)];
        }

        public void Set(int index, T item)
        {
            checkIndexOutOfRange(index);
            buffer[toBufferIndex(index)] = item;
        }

    }
}


//Template by _Trung_Nguyen
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using System.IO;

using Libraries;
using Libraries.IO;
using Libraries.Collections;

namespace Codeforces
{
	public class sample
	{
		private static int solution()
		{
			int n = cin.ReadInt32();

			int [] f = cin.ReadInt32Array();
			for (int i = 0; i < n; ++i) --f[i];

			int [] from = Arrays.Fill(n, -1).ToArray();

			for (int i = 0; i < n; ++i) from[f[i]] = i;

			bool [] visited = new bool[n];

			List<int> start = new List<int>();
			List<int> end = new List<int>();

			for (int i = 0; i < n; ++i)
			{
				if (from[i] != -1) continue;
				if (visited[i]) continue;

				start.Add(i);

				int current = i;
				while (!visited[f[current]])
				{
					visited[current] = true;
					current = f[current];
				}

				visited[current] = true;

				end.Add(current);
			}

			//Cycles
			for (int i = 0; i < n; ++i)
			{
				if (visited[i]) continue;

				start.Add(i);
				end.Add(i);

				int current = i;
				while (!visited[f[current]])
				{
					visited[current] = true;
					current = f[current];
				}

				visited[current] = true;
			}

			int k = start.Count;
			cout.WriteLine(k);

			for (int i = 0; i < k; ++i)
			{
				cout.WriteLine("{0} {1}", end[i] + 1, start[(i + 1) % k] + 1);
			}

			return 0;
		}

//===============Prewritten code======================
		private static istream cin = new istream(Console.OpenStandardInput());
		private static ostream cout = new ostream(Console.OpenStandardOutput());
		private static ostream cerr = new ostream(Console.OpenStandardError());
		private static ostream clog = new ostream(Console.OpenStandardError(), true);

		public static int Main()
		{
			try
			{
				int returnValue = solution();
				cout.Flush();
				cerr.Flush();
				return returnValue;
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
				return -1;
			}
		}
	}
}

namespace Libraries.IO
{
	public class istream : IDisposable
	{
		private BufferedStream buffer;
		private StreamReader reader;
		private Queue<string> words = new Queue<string>();
		private void getWords()
		{
			while (words.Count == 0)
			{
				string [] temp = reader.ReadLine().Split();
				foreach (var str in temp)
				{
					if (str.Length == 0) continue;
					words.Enqueue(str);
				}
			}
		}
		public istream(Stream istr)
		{
			buffer = new BufferedStream(istr);
			reader = new StreamReader(buffer);
		}
		public int Peek()
		{
			return reader.Peek();
		}
		public int Read()
		{
			return reader.Read();
		}
		public string ReadLine()
		{
			return reader.ReadLine();
		}
		public int [] ReadInt32Array()
		{
			return reader.ReadLine().Split().Select(x => Convert.ToInt32(x)).ToArray();
		}
		public long [] ReadInt64Array()
		{
			return reader.ReadLine().Split().Select(x => Convert.ToInt64(x)).ToArray();
		}
		public string [] ReadStringArray()
		{
			return reader.ReadLine().Split();
		}
		public int ReadInt32()
		{
			getWords();
			return Convert.ToInt32(words.Dequeue());
		}
		public long ReadInt64()
		{
			getWords();
			return Convert.ToInt64(words.Dequeue());
		}
		public string ReadString()
		{
			getWords();
			return words.Dequeue();
		}
		public void Dispose()
		{
			reader.Dispose();
			buffer.Dispose();
		}
	}
	public class ostream : IDisposable
	{
		private BufferedStream buffer;
		private StreamWriter writer;
		public ostream(Stream ostr, bool autoflush = false)
		{
			buffer = new BufferedStream(ostr);
			writer = new StreamWriter(buffer);
			writer.AutoFlush = autoflush;
		}
		public void Write(string format, params object[] args)
		{
			writer.Write(format, args);
		}
		public void Write<T>(T value)
		{
			writer.Write(value);
		}
		public void Write<T>(T [] arr, string sep)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, x);
		}
		public void WriteLine(string format, params object[] args)
		{
			writer.WriteLine(format, args);
		}
		public void WriteLine<T>(T value)
		{
			writer.WriteLine(value);
		}
		public void WriteLine<T>(IEnumerable<T> arr, string sep)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, x);
			writer.WriteLine();
		}
		public void WriteLine<T>(IEnumerable<T> arr, string sep, Func<T, T> funct)
		{
			string fmt = "{0}" + sep;
			foreach (var x in arr) writer.Write(fmt, funct(x));
			writer.WriteLine();
		}
		public void WriteLine()
		{
			writer.WriteLine();
		}
		public void Flush()
		{
			writer.Flush();
		}
		public void Dispose()
		{
			writer.Dispose();
			buffer.Dispose();
		}
	}
}
namespace Libraries
{
	public class Utilities
	{
		public static void Swap<T>(ref T x, ref T y)
		{
			T temp = y;
			y = x;
			x = temp;
		}
	}

	public static class Arrays
	{
		private static T Inc<T>(ref T value)
		{
			dynamic temp = value;
			dynamic temp2 = temp;
			value = ++temp2;
			return temp++;
		}
		public static IEnumerable<T> FillDefault<T>(int size) where T : new()
		{
			for (int i = 0; i < size; ++i) yield return new T();
		}
		public static IEnumerable<T> Fill<T>(int size, T value)
		{
			for (int i = 0; i < size; ++i) yield return value;
		}
		public static IEnumerable<T> Iota<T>(int size, T init)
		{
			for (int i = 0; i < size; ++i) yield return Inc(ref init);
		}
		public static T First<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			return container[0];
		}
		public static T Last<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			return container[container.Count - 1];
		}
		public static void RemoveLast<T> (this IList<T> container)
		{
			if (container.Count == 0) throw new InvalidOperationException("The container is empty");
			container.RemoveAt(container.Count - 1);
		}
	}
}

namespace Libraries.Collections
{
	class PriorityQueue<T> where T : IComparable
	{
	    private List<T> list;
	    public int Count { get { return list.Count; } }
	    public readonly bool IsDescending;

	    public PriorityQueue()
	    {
	        list = new List<T>();
	    }

	    public PriorityQueue(bool isdesc)
	        : this()
	    {
	        IsDescending = isdesc;
	    }

	    public PriorityQueue(int capacity)
	        : this(capacity, false)
	    { }

	    public PriorityQueue(IEnumerable<T> collection)
	        : this(collection, false)
	    { }

	    public PriorityQueue(int capacity, bool isdesc)
	    {
	        list = new List<T>(capacity);
	        IsDescending = isdesc;
	    }

	    public PriorityQueue(IEnumerable<T> collection, bool isdesc)
	        : this()
	    {
	        IsDescending = isdesc;
	        foreach (var item in collection)
	            Enqueue(item);
	    }

	    public void Enqueue(T x)
	    {
	        list.Add(x);
	        int i = Count - 1;

	        while (i > 0)
	        {
	            int p = (i - 1) / 2;
	            if ((IsDescending ? -1 : 1) * list[p].CompareTo(x) <= 0) break;

	            list[i] = list[p];
	            i = p;
	        }

	        if (Count > 0) list[i] = x;
	    }

	    public T Dequeue()
	    {
	        T target = Peek();
	        T root = list[Count - 1];
	        list.RemoveAt(Count - 1);

	        int i = 0;
	        while (i * 2 + 1 < Count)
	        {
	            int a = i * 2 + 1;
	            int b = i * 2 + 2;
	            int c = b < Count && (IsDescending ? -1 : 1) * list[b].CompareTo(list[a]) < 0 ? b : a;

	            if ((IsDescending ? -1 : 1) * list[c].CompareTo(root) >= 0) break;
	            list[i] = list[c];
	            i = c;
	        }

	        if (Count > 0) list[i] = root;
	        return target;
	    }

	    public T Peek()
	    {
	        if (Count == 0) throw new InvalidOperationException("Queue is empty.");
	        return list[0];
	    }

	    public void Clear()
	    {
	        list.Clear();
	    }
	}
    internal class DequeUtility
    {
        public static int ClosestPowerOfTwoGreaterThan(int x)
        {
            x--;
            x |= (x >> 1);
            x |= (x >> 2);
            x |= (x >> 4);
            x |= (x >> 8);
            x |= (x >> 16);
            return (x+1);
        }

        public static int Count<TSource>(IEnumerable<TSource> source)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            ICollection<TSource> genericCollection = source as ICollection<TSource>;
            if (genericCollection != null)
            {
                return genericCollection.Count;
            }

            ICollection nonGenericCollection = source as ICollection;
            if (nonGenericCollection != null)
            {
                return nonGenericCollection.Count;
            }

            checked
            {
                int count = 0;
                using (var iterator = source.GetEnumerator())
                {
                    while (iterator.MoveNext())
                    {
                        count++;
                    }
                }
                return count;
            }
        }
    }

    public class Deque<T> : IList<T>
    {
        private const int defaultCapacity = 16;

        private int startOffset;

        private T[] buffer;

        public Deque() : this(defaultCapacity) { }

        public Deque(int capacity)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "capacity", "capacity is less than 0.");
            }

            this.Capacity = capacity;
        }

        public Deque(IEnumerable<T> collection)
            : this(DequeUtility.Count(collection))
        {
            InsertRange(0, collection);
        }

        private int capacityClosestPowerOfTwoMinusOne;

        public int Capacity
        {
            get
            {
                return buffer.Length;
            }

            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException(
                        "value",
                        "Capacity is less than 0.");
                }
                else if (value < this.Count)
                {
                    throw new InvalidOperationException(
                        "Capacity cannot be set to a value less than Count");
                }
                else if (null != buffer && value == buffer.Length)
                {
                    return;
                }

                int powOfTwo = DequeUtility.ClosestPowerOfTwoGreaterThan(value);

                value = powOfTwo;

                T[] newBuffer = new T[value];
                this.CopyTo(newBuffer, 0);

                buffer = newBuffer;
                startOffset = 0;
                this.capacityClosestPowerOfTwoMinusOne = powOfTwo - 1;
            }
        }

        public bool IsFull
        {
            get { return this.Count == this.Capacity; }
        }

        public bool IsEmpty
        {
            get { return 0 == this.Count; }
        }

        private void ensureCapacityFor(int numElements)
        {
            if (this.Count + numElements > this.Capacity)
            {
                this.Capacity = this.Count + numElements;
            }
        }

        private int toBufferIndex(int index)
        {
            int bufferIndex;

            bufferIndex = (index + this.startOffset)
                & this.capacityClosestPowerOfTwoMinusOne;

            return bufferIndex;
        }

        private void checkIndexOutOfRange(int index)
        {
            if (index >= this.Count)
            {
                throw new IndexOutOfRangeException(
                    "The supplied index is greater than the Count");
            }
        }

        private static void checkArgumentsOutOfRange(
            int length,
            int offset,
            int count)
        {
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "offset", "Invalid offset " + offset);
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "count", "Invalid count " + count);
            }

            if (length - offset < count)
            {
                throw new ArgumentException(
                    String.Format(
                    "Invalid offset ({0}) or count + ({1}) "
                    + "for source length {2}",
                    offset, count, length));
            }
        }

        private int shiftStartOffset(int value)
        {
            this.startOffset = toBufferIndex(value);

            return this.startOffset;
        }

        private int preShiftStartOffset(int value)
        {
            int offset = this.startOffset;
            this.shiftStartOffset(value);
            return offset;
        }

        private int postShiftStartOffset(int value)
        {
            return shiftStartOffset(value);
        }

        #region IEnumerable

        public IEnumerator<T> GetEnumerator()
        {
            if (this.startOffset + this.Count > this.Capacity)
            {
                for (int i = this.startOffset; i < this.Capacity; i++)
                {
                    yield return buffer[i];
                }

                int endIndex = toBufferIndex(this.Count);
                for (int i = 0; i < endIndex; i++)
                {
                    yield return buffer[i];
                }
            }
            else
            {
                int endIndex = this.startOffset + this.Count;
                for (int i = this.startOffset; i < endIndex; i++)
                {
                    yield return buffer[i];
                }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion

        #region ICollection

        bool ICollection<T>.IsReadOnly
        {
            get { return false; }
        }

        public int Count
        {
            get;
            private set;
        }

        private void incrementCount(int value)
        {
            this.Count = this.Count + value;
        }

        private void decrementCount(int value)
        {
            this.Count = Math.Max(this.Count - value, 0);
        }

        public void Add(T item)
        {
            AddLast(item);
        }

        private void ClearBuffer(int logicalIndex, int length)
        {
            int offset = toBufferIndex(logicalIndex);
            if (offset + length > this.Capacity)
            {
                int len = this.Capacity - offset;
                Array.Clear(this.buffer, offset, len);

                len = toBufferIndex(logicalIndex + length);
                Array.Clear(this.buffer, 0, len);
            }
            else
            {
                Array.Clear(this.buffer, offset, length);
            }
        }

        public void Clear()
        {
            if (this.Count > 0)
            {
                ClearBuffer(0, this.Count);
            }
            this.Count = 0;
            this.startOffset = 0;
        }

        public bool Contains(T item)
        {
            return this.IndexOf(item) != -1;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            if (null == array)
            {
                throw new ArgumentNullException("array", "Array is null");
            }

            if (null == this.buffer)
            {
                return;
            }

            checkArgumentsOutOfRange(array.Length, arrayIndex, this.Count);

            if (0 != this.startOffset
                && this.startOffset + this.Count >= this.Capacity)
            {
                int lengthFromStart = this.Capacity - this.startOffset;
                int lengthFromEnd = this.Count - lengthFromStart;

                Array.Copy(
                    buffer, this.startOffset, array, 0, lengthFromStart);

                Array.Copy(
                    buffer, 0, array, lengthFromStart, lengthFromEnd);
            }
            else
            {
                Array.Copy(
                    buffer, this.startOffset, array, 0, Count);
            }
        }

        public bool Remove(T item)
        {
            bool result = true;
            int index = IndexOf(item);

            if (-1 == index)
            {
                result = false;
            }
            else
            {
                RemoveAt(index);
            }

            return result;
        }

        #endregion

        #region List<T>

        public T this[int index]
        {
            get
            {
                return this.Get(index);
            }

            set
            {
                this.Set(index, value);
            }
        }

        public void Insert(int index, T item)
        {
            ensureCapacityFor(1);

            if (index == 0)
            {
                AddFirst(item);
                return;
            }
            else if (index == Count)
            {
                AddLast(item);
                return;
            }

            InsertRange(index, new[] { item });
        }

        public int IndexOf(T item)
        {
            int index = 0;
            foreach (var myItem in this)
            {
                if (myItem.Equals(item))
                {
                    break;
                }
                ++index;
            }

            if (index == this.Count)
            {
                index = -1;
            }

            return index;
        }

        public void RemoveAt(int index)
        {
            if (index == 0)
            {
                RemoveFirst();
                return;
            }
            else if (index == Count - 1)
            {
                RemoveLast();
                return;
            }

            RemoveRange(index, 1);
        }
        #endregion

        public void AddFirst(T item)
        {
            ensureCapacityFor(1);
            buffer[postShiftStartOffset(-1)] = item;
            incrementCount(1);
        }

        public void AddLast(T item)
        {
            ensureCapacityFor(1);
            buffer[toBufferIndex(this.Count)] = item;
            incrementCount(1);
        }

        public T RemoveFirst()
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }

            T result = buffer[this.startOffset];
            buffer[preShiftStartOffset(1)] = default(T);
            decrementCount(1);
            return result;
        }

        public T RemoveLast()
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }

            decrementCount(1);
            int endIndex = toBufferIndex(this.Count);
            T result = buffer[endIndex];
            buffer[endIndex] = default(T);

            return result;
        }

        public void AddRange(IEnumerable<T> collection)
        {
            AddLastRange(collection);
        }

        public void AddFirstRange(IEnumerable<T> collection)
        {
            AddFirstRange(collection, 0, DequeUtility.Count(collection));
        }

        public void AddFirstRange(
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            InsertRange(0, collection, fromIndex, count);
        }

        public void AddLastRange(IEnumerable<T> collection)
        {
            AddLastRange(collection, 0, DequeUtility.Count(collection));
        }

        public void AddLastRange(
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            InsertRange(this.Count, collection, fromIndex, count);
        }

        public void InsertRange(int index, IEnumerable<T> collection)
        {
            int count = DequeUtility.Count(collection);
            this.InsertRange(index, collection, 0, count);
        }

        public void InsertRange(
            int index,
            IEnumerable<T> collection,
            int fromIndex,
            int count)
        {
            checkIndexOutOfRange(index - 1);

            if (0 == count)
            {
                return;
            }

            ensureCapacityFor(count);

            if (index < this.Count / 2)
            {
                if (index > 0)
                {
                    int copyCount = index;
                    int shiftIndex = this.Capacity - count;
                    for (int j = 0; j < copyCount; j++)
                    {
                        buffer[toBufferIndex(shiftIndex + j)] =
                            buffer[toBufferIndex(j)];
                    }
                }

                this.shiftStartOffset(-count);

            }
            else
            {
                if (index < this.Count)
                {
                    int copyCount = this.Count - index;
                    int shiftIndex = index + count;
                    for (int j = 0; j < copyCount; j++)
                    {
                        buffer[toBufferIndex(shiftIndex + j)] =
                            buffer[toBufferIndex(index + j)];
                    }
                }
            }

            int i = index;
            foreach (T item in collection)
            {
                buffer[toBufferIndex(i)] = item;
                ++i;
            }

            incrementCount(count);
        }

        public void RemoveRange(int index, int count)
        {
            if (this.IsEmpty)
            {
                throw new InvalidOperationException("The Deque is empty");
            }
            if (index > Count - count)
            {
                throw new IndexOutOfRangeException(
                    "The supplied index is greater than the Count");
            }

            ClearBuffer(index, count);

            if (index == 0)
            {
                this.shiftStartOffset(count);
                this.Count -= count;
                return;
            }
            else if (index == Count - count)
            {
                this.Count -= count;
                return;
            }

            if ((index + (count / 2)) < Count / 2)
            {
                int copyCount = index;
                int writeIndex = count;
                for (int j = 0; j < copyCount; j++)
                {
                    buffer[toBufferIndex(writeIndex + j)]
                        = buffer[toBufferIndex(j)];
                }

                this.shiftStartOffset(count);
            }
            else
            {
                int copyCount = Count - count - index;
                int readIndex = index + count;
                for (int j = 0; j < copyCount; ++j)
                {
                    buffer[toBufferIndex(index + j)] =
                        buffer[toBufferIndex(readIndex + j)];
                }
            }

            decrementCount(count);
        }
        public T Get(int index)
        {
            checkIndexOutOfRange(index);
            return buffer[toBufferIndex(index)];
        }

        public void Set(int index, T item)
        {
            checkIndexOutOfRange(index);
            buffer[toBufferIndex(index)] = item;
        }

    }
}