algo/quickmaffs-permutation-puzzle

74 solves / 465 points

Writeup by Aryan

Once again, the problem statement is quite simple. We wish to compute the number of permutations p of the numbers 1 ... n (n < 10^4) that satisfy the following:

  • For all i (2 <= i <= n), p[i - 1] < p[i] if i is odd, and p[i - 1] > p[i] if i is even (assume 1-indexing)

Since this number is quite large, we must return the result mod 10^9 + 7.

A simple python bruteforce for i in [1, 8] gives us the following interesting looking sequence: 1, 1, 2, 5, 16, 61, 272, 1385.

import itertools
MOD = 10**9 + 7

def ok(perm):
    for i in range(1, len(perm)):
        if i % 2 == 1:
            if perm[i-1] >= perm[i]:
                return False
        else:
            if perm[i-1] <= perm[i]:
                return False
    return True

def perms(n):
    all_permutations = itertools.permutations(range(1, n+1))
    valid_count = 0
    
    for perm in all_permutations:
        if ok(perm):
            valid_count += 1
    
    return valid_count % MOD

for i in range(1, 9):
    print(perms(i))

Turns out this is sequence A000111 on OEIS. Scrolling through the page, alternating permutations are mentioned a bunch. Googling alternating permutations leads to this GeeksForGeeks article that has most of the solution. All we need to do is implement some modular arithmetic to prevent overflow.

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll MOD = 1e9 + 7;

ll binpow(ll base, ll exp, ll mod) {
    ll result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (result * base) % mod;
        base = (base * base) % mod;
        exp /= 2;
    }
    return result;
}

void precompute_factorials(ll fact[], ll inv_fact[], int n) {
    fact[0] = 1;
    for (int i = 1; i <= n; i++) {
        fact[i] = (fact[i - 1] * i) % MOD;
    }
    inv_fact[n] = binpow(fact[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 0; i--) {
        inv_fact[i] = (inv_fact[i + 1] * (i + 1)) % MOD;
    }
}

ll binomial_coefficient(ll fact[], ll inv_fact[], int n, int k) {
    if (k > n || k < 0) return 0;
    return fact[n] * inv_fact[k] % MOD * inv_fact[n - k] % MOD;
}

ll zig_zag(int n) {
    ll fact[n + 1], inv_fact[n + 1], zig[n + 1] = {0};
    
    precompute_factorials(fact, inv_fact, n);
    
    zig[0] = 1;
    zig[1] = 1;
    
    for (int i = 2; i <= n; i++) {
        ll sum = 0;
        for (int k = 0; k <= i - 1; k++) {
            ll binom = binomial_coefficient(fact, inv_fact, i - 1, k);
            sum = (sum + (binom * zig[k] % MOD) * zig[i - 1 - k] % MOD) % MOD;
        }
        zig[i] = sum * binpow(2, MOD - 2, MOD) % MOD;
    }
    
    return zig[n];
}

int main() {
    int n;
    cin >> n;
    cout << zig_zag(n) << endl;
    return 0;
}

Flag: vsctf{QuickMaffs_FMC_Huge_Degen_b4a1c2af876eeb9a}

Last updated