Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Solving Inversion Counting, Permutation Optimization, and Other Competitive Programming Challenges

Tech May 14 1

Interval Inversion Count

Given an array of length n with elements bounded by a small constant (≤ 50), we need to answer m queries of the form: count the number of inversions inside subarray [l, r]. A naive O(n2) per query is too slow, and a Mo’s algorithm solution, while feasible for moderate constraints, will not handle the worst case. The key observation is that we can precompute prefix inversions if we manage to subtract cross‑inversions efficiently.

Let inv[i] be the total number of inversions in the first i elements, and cnt[i][v] be how many times value v appears among the first i elements. For a query [l, r], the inversions inside [l, r] equal inv[r] - inv[l-1] - cross, where cross counts pairs with the first element in [1, l−1] and the second in [l, r] that form an inversion. We compute cross by iterating values from largest to smallest, maintaining a running sum of the occurrences in the left prefix. For each value v, the left prefix contributes sum_left_so_far × (cnt[r][v] - cnt[l-1][v]). This gives an O(n · maxA + m · maxA) solution, easily fitting the limits.

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

const int MAXN = 1e6 + 5;
const int MAXV = 55;
int pref_cnt[MAXN][MAXV];
long long inv_prefix[MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        int x; cin >> x;
        ++x; // shift to 1-based value
        for (int v = 1; v < MAXV; ++v)
            pref_cnt[i][v] = pref_cnt[i-1][v];
        int greater_cnt = 0;
        for (int v = x + 1; v < MAXV; ++v)
            greater_cnt += pref_cnt[i][v];
        inv_prefix[i] = inv_prefix[i-1] + greater_cnt;
        pref_cnt[i][x]++;
    }
    while (m--) {
        int l, r; cin >> l >> r;
        long long cross = 0, left_acc = 0;
        for (int v = MAXV - 1; v >= 1; --v) {
            cross += left_acc * (pref_cnt[r][v] - pref_cnt[l-1][v]);
            left_acc += pref_cnt[l-1][v];
        }
        cout << inv_prefix[r] - inv_prefix[l-1] - cross << '\n';
    }
    return 0;
}

Permutation with Limited Moves

We are given a permutation of {1,…,n} and an integer m. Elements outside the range [1,m] must be moved either to position 1 or to position m, and whenever an element moves to one of these fixed positions, the previous occupant shifts cyclically. The goal is to minimize the total number of unit‑distance moves. A useful insight is that for any number i not yet inside [1,m], the optimal move is always to bring it direct to position 1 or m. This leads to a DP state dp[i][0] = minimal cost when i is placed at position 1, and dp[i][1] for position m.

Transitions are not simply from i to i+1, because once i is fixed, several subsequent numbers might already lie inside [1,m] and need no move. Hence we jump to the smallest number k > i that is currently outside [1,m] and treat it as the next decision point. This k can be found with a segment tree that maintains the minimum value in a sliding window over the circular arrangement. After processing each i, we set its position to a large sentinel so it won’t be chosen again. The DP thus runs in O(n log n).

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 500005;
const int INF = 1e9;

int n, m;
int pos[MAXN * 2];
ll dp[MAXN][2];
struct SegTree {
    vector<int> tree;
    int sz;
    SegTree(int n) : sz(n) { tree.assign(2 * n, INF); }
    void build() {
        for (int i = 0; i < sz; ++i) tree[i + sz] = pos[i + 1];
        for (int i = sz - 1; i; --i) tree[i] = min(tree[i<<1], tree[i<<1|1]);
    }
    int query(int l, int r) {
        int res = INF;
        for (l += sz - 1, r += sz - 1; l <= r; l >>= 1, r >>= 1) {
            if (l & 1) res = min(res, tree[l++]);
            if (!(r & 1)) res = min(res, tree[r--]);
        }
        return res;
    }
    void update(int p, int val) {
        p += sz - 1;
        tree[p] = val;
        for (p >>= 1; p; p >>= 1) tree[p] = min(tree[p<<1], tree[p<<1|1]);
    }
};

int dist_on_circle(int a, int b) {
    if (a <= 0) a += n;
    if (a > n) a -= n;
    int fwd = (a >= b ? a - b : a + n - b);
    int bwd = (b >= a ? b - a : n - a + b);
    return fwd < bwd ? fwd : -bwd;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        int x; cin >> x;
        pos[x] = i;
        pos[x + n] = i;
    }
    SegTree seg(2 * n);
    seg.build();
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0; // virtual 0 at position 1
    ll ans = LLONG_MAX;
    for (int i = 0; i < n; ++i) {
        int loc_i = (i == 0 ? 1 : pos[i]);
        // from dp[i][0] (i at position 1)
        int nxt = seg.query(loc_i + m, loc_i + n - 1);
        if (nxt == INF) ans = min(ans, dp[i][0]);
        else {
            int loc_nxt = pos[nxt];
            int shift1 = dist_on_circle(loc_nxt - dist_on_circle(loc_i, 1), 1);
            dp[nxt][0] = min(dp[nxt][0], dp[i][0] + abs(shift1));
            shift1 = dist_on_circle(loc_nxt - dist_on_circle(loc_i, 1), m);
            dp[nxt][1] = min(dp[nxt][1], dp[i][0] + abs(shift1));
        }
        // from dp[i][1] (i at position m)
        nxt = seg.query(loc_i + 1, loc_i + n - m);
        if (nxt == INF) ans = min(ans, dp[i][1]);
        else {
            int loc_nxt = pos[nxt];
            int shift2 = dist_on_circle(loc_nxt - dist_on_circle(loc_i, m), 1);
            dp[nxt][0] = min(dp[nxt][0], dp[i][1] + abs(shift2));
            shift2 = dist_on_circle(loc_nxt - dist_on_circle(loc_i, m), m);
            dp[nxt][1] = min(dp[nxt][1], dp[i][1] + abs(shift2));
        }
        if (i) {
            seg.update(pos[i], INF);
            seg.update(pos[i] + n, INF);
        }
    }
    ans = min({ans, dp[n][0], dp[n][1]});
    cout << ans << '\n';
    return 0;
}

Camellia – Maximum XOR with One Increment

We are given n integers. We may choose a subset, XOR them together, and then we are allowed to add 1 to any one of the chosen numbers (i.e., increment a value before XORing, if it is part of the subset). The goal is to maximize the final XOR result. The plus‑one operation is most beneficial when it causes a long carry chain, i.e. when the number ends with many consecutive 1‑bits. Hence we want to construct a XOR that ends with a long suffix of 1’s, so that incrementing that element triggers a cascade of bit flips. This suggests building a linear basis that operates from the least significant bit upward, i.e. a low‑priority basis. After building it, we check from the most significant bit downward whether we can achieve a suffix of ones of length len. If possible, we replace the suffix with the carry effect and output the result.

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

ll basis[61];

void insert_low(ll x) {
    for (int b = 0; b <= 60; ++b) {
        if ((x >> b) & 1) {
            if (basis[b]) x ^= basis[b];
            else { basis[b] = x; return; }
        }
    }
}

ll build_suffix(int len) {
    ll res = 0;
    for (int b = 0; b <= len; ++b) {
        int need = (b == len) ? 0 : 1;
        if (((res >> b) & 1) == need) continue;
        if (basis[b]) res ^= basis[b];
        else return -1;
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    ll overall_xor = 0;
    for (int i = 0; i < n; ++i) {
        ll v; cin >> v;
        overall_xor ^= v;
        insert_low(v);
    }
    for (int len = 60; len >= 0; --len) {
        if ((overall_xor >> len) & 1) continue;
        ll pat = build_suffix(len);
        if (pat != -1) {
            overall_xor ^= ((1LL << (len + 1)) - 1);
            cout << overall_xor << '\n';
            return 0;
        }
    }
    cout << overall_xor + 1 << '\n';
    return 0;
}

Medians with Element Deletions

We process a permutation of 1…n by successively deleting elements in reverse order and maintaining the median of the current set. Inserting an element and keeping the median is hard to do in O(1), but deleting one element is easier if we work on a sorted structure. Since the domain is exactly {1,…,n} and all values are distinct, a linked list over the value domain gives an ordered representation. We maintain the predecessor and successor of each value. Starting from the whole set, we know the initial median. As we delete the element originally at position i (in the deletion sequence), we adjust the median by at most one step to the left or right depending on whether the deleted element was smaller, equal, or larger than the current median. The total complexity is O(n).

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

const int MAXN = 1e7 + 5;
const int MOD = 998244353;
const int MOD7 = 1e9 + 7;
const int MOD9 = 1e9 + 9;

int p[MAXN], h[MAXN];
struct Node { int prev, next; } dom[MAXN]; // domain list

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, a;
    cin >> n >> a;
    for (int i = 1; i <= n; ++i) p[i] = i;
    for (int i = 1; i <= n; ++i) {
        a = (1LL * a * MOD + MOD7) % MOD9;
        swap(p[i], p[a % i + 1]);
    }
    h[0] = 1;
    for (int i = 1; i <= n; ++i) {
        dom[i].prev = i - 1;
        dom[i].next = i + 1;
        h[i] = 1LL * h[i-1] * 19 % MOD;
    }
    int median = (n + 1) / 2;
    int smaller = median - 1, bigger = n - median;
    long long ans = 0;
    for (int i = n; i >= 1; --i) {
        // maintain correct median position
        while (smaller + 1 < (i + 1) / 2) {
            median = dom[median].next;
            ++smaller; --bigger;
        }
        while (smaller + 1 > (i + 1) / 2) {
            median = dom[median].prev;
            --smaller; ++bigger;
        }
        ans = (ans + 1LL * (median % MOD) * h[i]) % MOD;
        int val = p[i];
        if (val < median) --smaller;
        else if (val == median) {
            if (dom[median].prev == 0) {
                median = dom[median].next;
                --bigger;
            } else {
                median = dom[median].prev;
                --smaller;
            }
        } else --bigger;
        // delete from domain list
        dom[dom[val].prev].next = dom[val].next;
        dom[dom[val].next].prev = dom[val].prev;
    }
    cout << ans << '\n';
    return 0;
}

Construct – Equation xp² = ab + bc + ac

We need to find positive integers a, b, c satisfying xp² = ab + bc + ac. Rearranging gives xp² + a² = (a + b)(a + c). A natural attempt is to set a = p, yielding (x+1)p² = (p+b)(p+c), so we can choose b = x+1−p and c = p²−p. This works when x ≥ p.

When x < p, we try to reduce the effective modulus. Let m = p mod (x+1); then 0 ≤ m ≤ x. Setting a = m, the equation modulo x+1 still holds, giving b = x+1−m and c = (xp² + m²)/(x+1) − m. This yields positive integers unless p = x+1 (where m = 0) making a possibly non‑positive. For the special case p = x+1, a separate construction a = 6, b = p−3, c = p²−4p+6 works for p > 3 (otherwise no solution exists). The solution is entirely analytical and runs in O(1) per test case.

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T; cin >> T;
    while (T--) {
        ll x, p; cin >> x >> p;
        if (x >= p) {
            cout << p << ' ' << x + 1 - p << ' ' << p*p - p << '\n';
        } else {
            if (p == x + 1) {
                if (p <= 3) cout << "-1\n";
                else cout << 6 << ' ' << p - 3 << ' ' << p*p - 4*p + 6 << '\n';
            } else {
                ll m = p % (x + 1);
                cout << m << ' ' << x + 1 - m << ' '
                     << (x * p * p + m * m) / (x + 1) - m << '\n';
            }
        }
    }
    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.