Cut a permutation anywhere, take the larger element on each side, keep the smaller of the two. Do that at every cut and you get an array. The question is how many permutations produce a given one β and the answer falls out of noticing that the array can only have one shape.
The problem#
Given n and an array a of length nβ1, count the permutations p of (1...n) with
for every 1β€iβ€nβ1, modulo 998244353. Both n and the sum of n over all tests go to 10^6, so the budget is linear and the constant matters.
One of the two maxima is always n#
Write L_i=max_(jβ€i)p_j and R_i=max_(j>i)p_j. As the cut moves right, L can only grow and R can only shrink. That much is immediate. The useful observation is sharper: let k be the position holding the value n. Then n is on the right of every cut before k and on the left of every cut from k onwards, so
In words:
a_i is the maximum of whichever side does not contain n.
So a is a prefix-maximum sequence glued to a suffix-maximum sequence β non-decreasing, then non-increasing. It is bitonic, and it has no choice about it.
The peak is nβ1#
The two halves either side of position k partition {1,...,n}β{n} between them. One of them therefore contains nβ1, and
Exactly one of the two, never both, since nβ1 sits in exactly one half. That single fact does a lot of work below: it says the peak of the bitonic array is pinned to a known value, so an array whose peak is anything else is unachievable and the answer is zero.
Reading p back off a#
Walk the left half outward from index 1. Since a_i=max(p_1,...,p_i) there,
- if a_i>a_(iβ1), position i holds a new left-to-right maximum, and its value is forced to be exactly a_i;
- if a_i=a_(iβ1), position i holds something that did not break the record β any unused value below a_i, and we get to choose.
The right half is the mirror image. From a_j=max(p_(j+1),...,p_n), a step a_j>a_(j+1) forces p_(j+1)=a_j, and a_j=a_(j+1) leaves a free choice below a_j.1Index j on the right names position j+1, not j. Off-by-one here is the easiest way to write a solution that is right on palindromic inputs and wrong on everything else.
Each of the nβ1 entries of a names exactly one position of p, and the one position left over is k, which holds n.
Counting#
Process the entries in increasing order of value. Because EquationΒ 2 makes a rise and then fall, the two smallest unprocessed entries are always the two ends, so a pair of pointers walking inward visits the values in sorted order without ever sorting anything.
Keep a counter t of positions already assigned. When the value v at the current end is strictly larger than the previous one, that position is a record and its value is forced: one way. When v repeats the previous value, the position takes any unused value strictly below v. Of the vβ1 candidates, tβ1 are already spent β every assigned position so far holds a value β€v, and exactly one of them holds v itself β leaving
choices β EquationΒ 4. If that count reaches zero the array is unachievable. Multiply, and finish with a factor of 2: when the pointers meet, one entry and two positions remain, and the last freedom is which side of the final cut takes n.
The whole thing is a single inward walk: O(n) time and no allocation beyond the input.
The code#
Three of the four early exits are the conditions above β the peak must be nβ1 by EquationΒ 3, values must not decrease along the walk, and a repeat must leave something to choose.
const PRIME_MOD: u64 = 998_244_353;
fn count(n: usize, xs: &[u32]) -> u64 {
let (mut i, mut j, mut prev) = (0, n - 2, 0);
let mut res = 1;
let mut taken = 0;
loop {
// Equal ends can only be the peak, and the peak is pinned to n-1:
// two different halves cannot both have maximum v.
if xs[i] == xs[j] && xs[i] as usize != n - 1 {
return 0
};
if i == j { return res * 2 % PRIME_MOD };
let left = xs[i] < xs[j];
let v = if left { xs[i] } else { xs[j] };
if v < prev { return 0 }; // not bitonic
if taken >= v { return 0 }; // nothing left below v to choose
if v == prev {
res *= (v - taken) as u64; // a non-record position
res %= PRIME_MOD;
}
prev = v;
if left { i += 1 } else { j -= 1 };
taken += 1;
}
}The equality test at the top of the loop is doing double duty, which is worth pausing on. While the pointers are apart it rejects an array claiming two halves with the same maximum. When they meet it is trivially true, and so becomes the check that the peak really is nβ1 before the answer is returned.