## 1. Abridged problem statement

We have objects `1, 1', 2, 2', ..., n, n'`. A valid **bipermutation** is an ordering of all `2n` objects such that:

- each primed object `i'` appears before its unprimed object `i`;
- the relative order of primed objects is the same as the relative order of unprimed objects.

For `i != j`, define:

- `b_ij = j'` if `j < i`,
- `b_ij = j` if `j > i`.

For every `i`, `a(i)` is the number of `j != i` such that object `i` appears before object `b_ij`.

Given `a1, ..., an`, construct any valid bipermutation with these values, or print `NO`.

In the output, unprimed `i` is printed as `i`, and primed `i'` is printed as `-i`.

Constraints: `1 <= n <= 1000`.

---

## 2. Detailed editorial

### 2.1 Structure of a bipermutation

Because primed and unprimed objects have the same relative order, every bipermutation is determined by:

1. a permutation

```text
pi[1], pi[2], ..., pi[n]
```

which is the order of the unprimed objects;

2. positions where primed objects are inserted.

The primed objects must appear in the same order:

```text
pi[1]', pi[2]', ..., pi[n]'
```

Also, each `pi[r]'` must appear before `pi[r]`.

So a valid bipermutation is an interleaving of two equal-order copies of the same permutation, where each primed copy appears before its unprimed copy.

---

### 2.2 Contribution to `a(v)`

Suppose `v = pi[r]`.

For some `j > v`, we compare `v` with unprimed `j`.

So `j` contributes to `a(v)` iff unprimed `j` appears after `v`, i.e. `j` lies to the right of `v` in `pi`.

For some `j < v`, we compare `v` with primed `j'`.

So `j` contributes iff `j'` appears after unprimed `v`.

Therefore:

```text
a(v)
=
number of larger values already to the right of v in pi
+
number of smaller values to the right of v whose primes are also after v
```

This is the key formula.

---

### 2.3 Building the permutation from right to left

The solution constructs `pi` from right to left.

Let `picked` store already chosen values. Since we build from the end:

```text
picked[0] = pi[n]
picked[1] = pi[n - 1]
...
```

At some step `k`, we have already placed `k` values on the right.

For an unused value `v`, define:

```text
larger_picked[v] = number of already picked values greater than v
```

This is the first part of `a(v)`.

So the remaining needed contribution is:

```text
need = a[v] - larger_picked[v]
```

This must come from smaller already-picked values whose primes appear after `v`.

---

### 2.4 Representing prime positions using `g`

When placing a new value `v`, some suffix of the already picked values will have their primes after `v`.

Let:

```text
g = number of already picked values whose primes are after v
```

Because primed objects appear in the same order as unprimed objects, those `g` values are exactly:

```text
picked[0], picked[1], ..., picked[g - 1]
```

That is, the most recently picked values, i.e. the rightmost values in `pi`.

For every candidate `v`, we know positions of already picked smaller values:

```text
smaller_pos[v]
```

This vector contains indices in `picked` where values smaller than `v` occurred.

We need exactly `need` of those positions to be `< g`.

So if:

```text
smaller_pos[v] = [p0, p1, ..., p(m-1)]
```

then legal `g` must satisfy:

```text
p[need - 1] < g <= p[need]
```

with boundary cases:

- if `need = 0`, then `g` can start from `0`;
- if `need = total_smaller`, then `g` can go up to `k`.

Thus:

```text
g_lo = 0                         if need == 0
g_lo = smaller_pos[v][need-1]+1  otherwise

g_hi = k                         if need == total_smaller
g_hi = smaller_pos[v][need]      otherwise
```

Also, as we move from right to left, `g` cannot decrease. Therefore:

```text
g >= g_prev
```

So the actual minimal possible value is:

```text
cur_g = max(g_lo, g_prev)
```

Candidate `v` is legal if:

```text
cur_g <= g_hi
```

---

### 2.5 Greedy choice

At every step, among all legal unused values, choose the one with smallest possible `cur_g`.

Why?

- A smaller `g` leaves more freedom for future positions.
- Future positions require `g` to be nondecreasing, so increasing `g` too early can only hurt.
- If multiple values have the same `cur_g`, choose the smallest value. Picking a smaller value increases `larger_picked` for fewer remaining values, so it is safer.

If no legal candidate exists, the answer is `NO`.

---

### 2.6 Reconstructing the final sequence

After the greedy process:

```text
picked[0] = pi[n]
picked[1] = pi[n - 1]
...
picked[n - 1] = pi[1]
```

So:

```text
pi[r] = picked[n - r]
```

For each position `r`, the algorithm has stored `g`.

If there are `g` primes after `pi[r]`, then there are `n - g` primes before or at that point.

Define:

```text
f[r] = n - g_for_pi[r]
```

Then, before outputting unprimed `pi[r]`, output all primed objects:

```text
pi[1]', pi[2]', ..., pi[f[r]]'
```

that have not already been output.

Finally output `pi[r]`.

---

### 2.7 Complexity

There are `n` steps, and each step scans all `n` values.

Updating auxiliary arrays also costs `O(n)` per step.

Total complexity:

```text
O(n^2)
```

Memory complexity:

```text
O(n^2)
```

because `smaller_pos` may store `O(n^2)` total positions.

For `n <= 1000`, this is fine.

---

## 3. Provided C++ solution with detailed comments

```cpp
#include <bits/stdc++.h> // Includes almost all standard C++ headers.

using namespace std; // Allows using standard library names without std:: prefix.

// Output operator for pairs.
// Prints pair as: first second
template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

// Input operator for pairs.
// Reads first and second.
template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

// Input operator for vectors.
// Reads all elements of the vector.
template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) { // Iterate by reference over all vector elements.
        in >> x;      // Read current element.
    }
    return in;        // Return stream to allow chaining.
};

// Output operator for vectors.
// Prints all elements separated by spaces.
template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {        // Iterate over vector elements.
        out << x << ' ';    // Print element followed by a space.
    }
    return out;             // Return stream to allow chaining.
};

int n;          // Number of pairs of objects.
vector<int> a;  // Required values a[0], ..., a[n - 1].

// Reads input.
void read() {
    cin >> n;       // Read n.
    a.resize(n);    // Resize vector to hold n integers.
    cin >> a;       // Read all a[i] using overloaded vector input operator.
}

void solve() {
    // First, validate that every a[i] is in the possible range [0, n - 1].
    // For each i, there are only n - 1 possible j != i.
    for(int i = 0; i < n; i++) {
        if(a[i] < 0 || a[i] > n - 1) {
            cout << "NO\n";
            return;
        }
    }

    vector<int> picked;
    // picked stores the permutation pi from right to left:
    // picked[0] = pi[n], picked[1] = pi[n - 1], ...

    vector<bool> used(n + 1, false);
    // used[v] tells whether value v has already been placed.

    vector<int> larger_picked(n + 1, 0);
    // larger_picked[v] = number of already picked values greater than v.
    // These definitely contribute to a(v), because their unprimed copies
    // are to the right of v.

    vector<vector<int>> smaller_pos(n + 1);
    // smaller_pos[v] stores positions in picked where the picked value
    // is smaller than v.
    //
    // Example:
    // if picked = [7, 2, 5] and v = 6,
    // then smaller picked values are 2 and 5,
    // located at positions 1 and 2,
    // so smaller_pos[6] = [1, 2].

    vector<int> g(n, 0);
    // g[k] will store the chosen value of g when picked[k] was placed.
    //
    // g = number of already picked values whose primes are placed after
    // the current unprimed object.

    int g_prev = 0;
    // As we construct pi from right to left, g cannot decrease.

    // Build the unprimed permutation from right to left.
    for(int k = 0; k < n; k++) {
        // k = number of already picked values.

        int best = -1;
        // best will store the chosen value for this step.

        int best_g = INT_MAX;
        // best_g stores the smallest feasible g found so far.

        // Try every unused value v as the next value to place.
        for(int v = 1; v <= n; v++) {
            if(used[v]) {
                continue; // Cannot use a value twice.
            }

            // The first part of a(v) is already forced:
            // larger_picked[v] larger values are to the right of v.
            //
            // The remaining contribution must come from smaller values
            // whose primed copies are after v.
            int need = a[v - 1] - larger_picked[v];

            // Number of already picked values smaller than v.
            int total_smaller = (int)smaller_pos[v].size();

            // need must be between 0 and total_smaller.
            if(need < 0 || need > total_smaller) {
                continue;
            }

            // We need exactly `need` smaller picked values to be in the
            // first g positions of picked.
            //
            // If need > 0, g must be greater than the position of the
            // (need - 1)-th smaller value.
            int g_lo = (need == 0) ? 0 : smaller_pos[v][need - 1] + 1;

            // If need < total_smaller, g must be at most the position
            // of the need-th smaller value.
            //
            // If need == total_smaller, all smaller picked values may be
            // included, so g can go up to k.
            int g_hi = (need == total_smaller) ? k : smaller_pos[v][need];

            // g must also be at least g_prev because g is nondecreasing.
            int cur_g = max(g_lo, g_prev);

            // If even the smallest possible g is too large, v is illegal.
            if(cur_g > g_hi) {
                continue;
            }

            // Greedy rule:
            // choose candidate with smallest possible g.
            // Since v is scanned in increasing order and we update only
            // when cur_g < best_g, ties are automatically resolved in favor
            // of smaller v.
            if(cur_g < best_g) {
                best = v;
                best_g = cur_g;
            }
        }

        // If no candidate works, no valid bipermutation exists.
        if(best == -1) {
            cout << "NO\n";
            return;
        }

        // Place the chosen value.
        picked.push_back(best);

        // Mark it as used.
        used[best] = true;

        // Store the chosen g value for this position.
        g[k] = best_g;

        // Update monotonic lower bound for future positions.
        g_prev = best_g;

        // Update auxiliary information for all remaining values.
        for(int v = 1; v <= n; v++) {
            if(v < best) {
                // Since best is greater than v and is now placed to the right,
                // it contributes to larger_picked[v].
                larger_picked[v]++;
            } else if(v > best) {
                // Since best is smaller than v and is now placed,
                // remember its position in picked.
                smaller_pos[v].push_back(k);
            }
        }
    }

    vector<int> pi(n + 1), f(n + 1);
    // pi[1..n] will be the final unprimed permutation.
    // f[r] = number of primed objects that should appear before pi[r].

    for(int r = 1; r <= n; r++) {
        // picked stores pi in reverse order.
        pi[r] = picked[n - r];

        // If g values have primes after the current object,
        // then n - g primes are before it.
        f[r] = n - g[n - r];
    }

    vector<int> seq;
    // seq will store the final output order.

    seq.reserve(2 * n);
    // Reserve memory for exactly 2n objects to avoid reallocations.

    int next_primed = 1;
    // The next primed object to output is pi[next_primed]'.

    for(int r = 1; r <= n; r++) {
        // Before outputting unprimed pi[r], output primed objects
        // pi[next_primed]', ..., pi[f[r]]' that have not yet been output.
        while(next_primed <= f[r]) {
            seq.push_back(-pi[next_primed]);
            // Negative number denotes primed object.

            next_primed++;
            // Move to next primed object in prime order.
        }

        // Output the unprimed object pi[r].
        seq.push_back(pi[r]);
    }

    // A valid sequence has been constructed.
    cout << "YES\n";

    // Print the sequence.
    cout << seq << '\n';
}

int main() {
    ios_base::sync_with_stdio(false);
    // Speeds up C++ input/output.

    cin.tie(nullptr);
    // Unties cin from cout for faster input.

    int T = 1;
    // The problem has a single test case.

    // cin >> T;
    // This is commented out because the input format has no test count.

    for(int test = 1; test <= T; test++) {
        read();  // Read one test case.
        solve(); // Solve it.
    }

    return 0; // End program successfully.
}
```

---

## 4. Python solution with detailed comments

```python
import sys


def solve():
    # Read all integers from input.
    data = list(map(int, sys.stdin.read().split()))

    # First integer is n.
    n = data[0]

    # Next n integers are a[0], ..., a[n - 1].
    a = data[1:1 + n]

    # Each a[i] must be between 0 and n - 1.
    for x in a:
        if x < 0 or x > n - 1:
            print("NO")
            return

    # picked stores values of pi from right to left:
    # picked[0] = pi[n], picked[1] = pi[n - 1], ...
    picked = []

    # used[v] is True if value v is already placed.
    used = [False] * (n + 1)

    # larger_picked[v] = number of already picked values greater than v.
    larger_picked = [0] * (n + 1)

    # smaller_pos[v] stores indices in picked where values smaller than v appear.
    smaller_pos = [[] for _ in range(n + 1)]

    # g_values[k] stores chosen g when picked[k] is placed.
    g_values = [0] * n

    # g must be nondecreasing while building from right to left.
    g_prev = 0

    # Build the permutation from right to left.
    for k in range(n):
        # k is the number of already picked values.

        best = -1
        best_g = 10**18

        # Try all unused values as the next value.
        for v in range(1, n + 1):
            if used[v]:
                continue

            # Contributions from larger values already to the right are forced.
            # The remaining contribution must come from smaller values whose
            # primes are after v.
            need = a[v - 1] - larger_picked[v]

            # Number of already picked smaller values.
            total_smaller = len(smaller_pos[v])

            # need must be feasible.
            if need < 0 or need > total_smaller:
                continue

            # Find legal interval [g_lo, g_hi] for g.

            # If need == 0, no smaller value should be included,
            # so g can start from 0.
            # Otherwise, g must pass the (need - 1)-th smaller position.
            if need == 0:
                g_lo = 0
            else:
                g_lo = smaller_pos[v][need - 1] + 1

            # If need == total_smaller, all smaller values may be included,
            # so g can go up to k.
            # Otherwise, g must not pass the need-th smaller position.
            if need == total_smaller:
                g_hi = k
            else:
                g_hi = smaller_pos[v][need]

            # g cannot decrease.
            cur_g = max(g_lo, g_prev)

            # Candidate is invalid if the minimum possible g is too large.
            if cur_g > g_hi:
                continue

            # Greedy choice:
            # minimize cur_g.
            # Since v is scanned increasingly and we only update on strict
            # improvement, ties automatically choose the smallest v.
            if cur_g < best_g:
                best = v
                best_g = cur_g

        # No legal value means impossible.
        if best == -1:
            print("NO")
            return

        # Place best.
        picked.append(best)

        # Mark best as used.
        used[best] = True

        # Store chosen g.
        g_values[k] = best_g

        # Update g lower bound.
        g_prev = best_g

        # Update auxiliary information.
        for v in range(1, n + 1):
            if v < best:
                # best is greater than v and now lies to the right of v.
                larger_picked[v] += 1
            elif v > best:
                # best is smaller than v and appears at picked index k.
                smaller_pos[v].append(k)

    # Reconstruct pi[1..n] from picked.
    pi = [0] * (n + 1)

    # f[r] = number of primed objects that must appear before pi[r].
    f = [0] * (n + 1)

    for r in range(1, n + 1):
        # picked is reversed pi.
        pi[r] = picked[n - r]

        # If g primes are after pi[r], then n - g primes are before it.
        f[r] = n - g_values[n - r]

    # Construct final sequence.
    seq = []

    # next_primed is the next index in pi whose primed version is not output yet.
    next_primed = 1

    for r in range(1, n + 1):
        # Output all primed objects that should appear before pi[r].
        while next_primed <= f[r]:
            # Negative means primed.
            seq.append(-pi[next_primed])
            next_primed += 1

        # Output unprimed object.
        seq.append(pi[r])

    print("YES")
    print(*seq)


if __name__ == "__main__":
    solve()
```

---

## 5. Compressed editorial

A bipermutation is an interleaving of two copies of one permutation `pi`: the primed copy and the unprimed copy, with the same relative order, and each primed element before its unprimed element.

Build `pi` from right to left. Suppose `v` is placed now and `k` values are already placed to its right.

For `a(v)`:

- every already placed value greater than `v` contributes via its unprimed object;
- some already placed values smaller than `v` contribute if their primed objects are still after `v`.

Maintain:

```text
larger_picked[v] = number of picked values greater than v
smaller_pos[v] = positions in picked of picked values smaller than v
```

Then:

```text
need = a[v] - larger_picked[v]
```

must be the number of smaller picked values whose primes are after `v`.

Let `g` be the number of picked values whose primes are after `v`. These are exactly the prefix `picked[0..g-1]`.

Using `smaller_pos[v]`, compute the interval of valid `g` values:

```text
g_lo = 0 if need == 0 else smaller_pos[v][need - 1] + 1
g_hi = k if need == total_smaller else smaller_pos[v][need]
```

Also `g` must be nondecreasing while moving left, so:

```text
g >= g_prev
```

Candidate `v` is valid if:

```text
max(g_lo, g_prev) <= g_hi
```

Greedily choose the valid `v` with smallest possible `g`; break ties by smaller `v`.

If no value is valid, print `NO`.

Otherwise reconstruct `pi` by reversing `picked`. If `g` primes are after `pi[r]`, then `n - g` primes are before it. Output primed objects in order before each unprimed object accordingly.

Complexity: `O(n^2)`.