#include <bits/stdc++.h>
using namespace std;
/*
Thuật toán:
- Phân tách 1..n thành blocks theo biểu diễn nhị phân của n (kích thước là powers of two).
- Với mỗi block (power of two) chạy "tournament internal" để làm tất cả phần tử trong block bằng sum(block).
(thực hiện các thao tác ghép cặp như mô tả).
- Giữ danh sách blocks (mỗi block lưu vector indices).
- Lặp: nếu có hai block cùng kích thước s, merge chúng bằng s thao tác op(A[k], B[k]).
Nếu không có, lấy block lớn nhất và split thành 2 block bằng nhau (chỉ chỉnh vector indices, không tốn op).
- Lặp tới khi chỉ còn 1 block (kích thước n).
- In các thao tác. Nếu số thao tác > 15*n thì in -1 (dự phòng).
*/
using vi = vector<int>;
using pii = pair<int,int>;
vector<pii> ops;
// chạy tournament trên block có kích thước s = power of two
// indices: vector<int> kích thước s; thực hiện ops ghi vào global ops
void tournament_pow2(const vi &indices) {
int s = (int)indices.size();
// len = 1,2,4,... < s
for (int len = 1; len < s; len <<= 1) {
for (int start = 0; start < s; start += 2*len) {
for (int k = 0; k < len; ++k) {
int u = indices[start + k];
int v = indices[start + len + k];
ops.emplace_back(u, v);
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if(!(cin >> n)) return 0;
vector<long long> a(n+1);
for(int i=1;i<=n;i++) cin >> a[i];
// Step 1: partition 1..n into blocks of power-of-two sizes (by binary decomposition)
vector<vi> blocks; // each block is vector<int> indices
int cur = 1;
for (int b = 0; (1<<b) <= n; ++b) {
if ( (n >> b) & 1 ) {
int sz = (1<<b);
vi idx(sz);
for (int t = 0; t < sz; ++t) idx[t] = cur + t;
cur += sz;
blocks.push_back(idx);
}
}
// Step 2: run tournament internal on each power-of-two block
for (auto &blk : blocks) {
tournament_pow2(blk);
}
// Step 3: merge blocks by repeatedly pairing equal-size blocks; if none, split largest block
// We'll keep blocks in a multiset-like structure keyed by size; but we'll use vector and manual search
// because number of different blocks is <= number of bits (~15).
// For splitting, we just split indices vector into halves.
while ( (int)blocks.size() > 1 ) {
// try to find two blocks with same size
int m = blocks.size();
bool merged = false;
// map size -> index
unordered_map<int,int> mapSize;
mapSize.reserve(m*2);
for (int i = 0; i < m; ++i) {
int s = (int)blocks[i].size();
if (mapSize.find(s) == mapSize.end()) mapSize[s] = i;
else {
int j = mapSize[s];
if (j == i) continue;
// merge blocks[j] and blocks[i]
vi &A = blocks[j];
vi &B = blocks[i];
int ssz = s;
// pairwise ops
for (int k = 0; k < ssz; ++k) {
ops.emplace_back(A[k], B[k]);
}
// create merged block (concatenate indices)
vi M;
M.reserve(ssz*2);
for (int k = 0; k < ssz; ++k) M.push_back(A[k]);
for (int k = 0; k < ssz; ++k) M.push_back(B[k]);
// replace blocks[j] by M, remove blocks[i]
blocks[j] = move(M);
// erase blocks[i]
blocks.erase(blocks.begin() + i);
merged = true;
break;
}
}
if (!merged) {
// no two equal-size blocks -> split the largest block into two equal halves
int idxMax = 0;
int maxS = (int)blocks[0].size();
for (int i = 1; i < (int)blocks.size(); ++i) {
if ((int)blocks[i].size() > maxS) {
maxS = (int)blocks[i].size();
idxMax = i;
}
}
// split blocks[idxMax] into two halves
vi old = blocks[idxMax];
int half = maxS / 2;
vi A(old.begin(), old.begin() + half);
vi B(old.begin() + half, old.end());
blocks[idxMax] = move(A);
blocks.push_back(move(B));
// no operation added because splitting doesn't change values (elements equal inside block)
}
// safety: if ops too many, break and print -1 later
if ((int)ops.size() > 15 * n) break;
}
if ((int)ops.size() > 15 * n) {
cout << -1 << "\n";
return 0;
}
// Optional: (sanity) we can assert blocks.size() == 1 and blocks[0].size() == n
// Print operations
cout << ops.size() << "\n";
for (auto &p : ops) {
cout << p.first << " " << p.second << "\n";
}
return 0;
}