Educational Codeforces Round 183 (Rated for Div. 2) 补题记录

Educational Codeforces Round 183 (Rated for Div. 2) 补题记录

D - Inversion Value of a Permutation

题目大意:

排列的逆序值为这个排列中存在逆序对的子区间的个数。

构造一个 \(n\) 个数的排列,使排列的逆序值恰好等于 \(k\)

思路:

首先考虑如何计算答案。假设存在一组逆序对,\((p_i, p_j)\) \((1 \leq i, j \leq n)\) 其中 \(i + 1 = j\),我们可以发现这一组逆序对的贡献应该是 \(i\)。并且对于任意的逆序对 \((p_k, p_j)\) 其中 \(k < i\),他所代表的区间都已经被 \((p_i, p_j)\) 的贡献计算。因此我们得出结论:只需要统计相邻逆序对的贡献之和,即为排列的逆序值。

可以发现如果直接构造逆序值恰好为 \(k\) 的排列,难度很大。正难则反,我们可以考虑构造一个有 \(k' = \frac{n (n - 1)}{2} - k\) 个不存在逆序对的子区间的排列。

同时,我们知道:对于一个长度为 \(len\) 的连续递增子数组,他对 \(k'\) 的贡献为 \(\frac{len (len - 1)}{2}\)。因此便可以进一步转化为用 \(n\) 个数构造 \(m\) 个连续递增的子数组,其中第 \(i\) 个子数组的长度为 \(len_i\)。且存在:

\[\sum_{i = 1}^{m} \frac{len_i (len_i - 1)}{2} = k' \]

接着我们再进一步考虑如何构造最终答案,其实很简单。对于一个排列 \(p = {1, 2, \dots, n}\) 我们只需要把它分成 \(m\) 组且符合上述等式,再将其倒序排列然后输出即可。

最后,我们要解决的就只剩下答案的存在性了。也就是我们能否用 \(n\) 个数组成一个存在 \(k'\) 个不存在逆序对的区间的排列。可以考虑用 \(dp\) 来预处理。用 \(dp[i][j]\) 表示使用 \(i\) 个数字构造 \(j\) 个连续子数组的可行性,那么转移方程便是:

\[dp[i][j] = dp[i][j] || dp[i - 1][j - len] \]

其中,\(len\) 代表当前要构造一个长度为 \(len\) 的子数组。

code:

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

#define endl '\n'
#define i64 long long

const int N = 40, M = N * (N - 1) / 2;
int f[N][M], pre[N][M];

void init() {
    f[0][0] = 1;
    for (int i = 1; i < N; i ++ ) {
        for (int j = 0; j < M; j ++ ) {
            for (int len = 1; i - len >= 0 && j - len * (len - 1) / 2 >= 0; len ++ ) {
                if (f[i - len][j - len * (len - 1) / 2]) {
                    f[i][j] = 1;
                    pre[i][j] = len;
                }
            }
        }
    }
}

void MuBai() {
    int n, k;
    cin >> n >> k;

    k = n * (n - 1) / 2 - k;
    if (!f[n][k]) {
        cout << "0\n";
        return;
    }

    int nn = n, kk = k, last = 1;
    vector<int> ans;
    while (pre[nn][kk]) {
        int len = pre[nn][kk];
        for (int i = last + len - 1; i >= last; i -- ) {
            ans.emplace_back(i);
        }
        last = last + len;
        nn -= len, kk -= len * (len - 1) / 2;
    }

    for (int i = n - 1; i >= 0; i -- ) {
        cout << ans[i] << " \n"[i == 0];
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);

    init();
    
    int t; cin >> t;
    while (t -- ) MuBai();

    return 0;
}
posted @ 2025-11-26 09:41  算法蒟蒻沐小白  阅读(0)  评论(0)    收藏  举报