Loading

12.02 CW 模拟赛 T2.排列

前言

也是找到了韩国原题, 有用!

算法

场上有一个比较显然的想法, 即计算出每种逆序对数量对应多少排列, 从而计算出排名第 \(k\) 小的排列有多少个逆序对

但是即使计算出来了, 我们也不好实现, 分析原因发现, 实际上是因为不好确定应该怎么填数, 时间复杂度仍然趋势

一个显然的想法是, 我们在计算出前 \(i\) 位时, 需要相应的的去找后面的解, 那么思路就比较清晰了

首先, 我们需要计算出每种逆序对数量对应多少排列, 考虑 \(\rm{dp}\) (其实我自己不可能考虑的出来, 没有题解的话思考难度应该到 \(\color{#3498db}{提高+/省选−}\))

这个时候 \(\rm{HYH}\) 大佬正在讲这个题, 疑似可以打表找规律, 不管了

首先, 令 \(f_{i, j}\) 表示 \(i\) 个数的排列, 逆序对个数为 \(j\) 的方案数

我们考虑在 \(i - 1\) 的排列中插入 \(i\) 来递推 \(i\) 的状态, 显然的, 我们把 \(i\) 插入在 \(p \in [0, i - 1]\)\(i\) 个位置时, 逆序对个数都会 \(+ (i - p - 1)\)

那么有转移, 注意判断边界条件

\[f_{i, k} = \sum_{j = k - i + 1}^{k} f_{i - 1, j} \]

时空复杂度都为 \(\mathcal{O} (n \omega)\) , 其中 \(\omega = 200\) , 注意使用前缀和优化, 后面要用 \(i\) 这一维所以不能滚动数组

完事之后考虑推答案,

首先来说, 我们可以考虑从前往后加入数字, 以此来确定第 \(k\) 个全排列
利用先前推出的 \(f\) 数组, 我们可以知道, 每一位产生的逆序对个数, 具体的, 找到最小的逆序对个数 \(j\) , 使得中间经过的逆序对个数严格小于 \(k\) , 这是一个确定的数

然后我们就可以知道, 对于从前往后加入的情况, 每次需要制造的逆序对个数

我们可以知道, 对于确定的逆序对个数, 我们考虑加入一个数使其满足条件, 显然的, 当后面还有 \(j\) 个符合逆序对条件的数, 那么一定成立, 使用树状数组 + 二分处理, 这样可以构造唯一解


考虑复习,

当你处理完了 \(\rm{dp}\) 之后, 就可以考虑利用这些信息

对于所有长度 \(l\) 的排列, 我们都可以

  • 按照逆序对个数分层
  • 知道每一层的排列个数
  • 知道序列的逆序对个数 \(ans\)
  • 知道序列在当前逆序对个数这一层的排名

一个对于逆序对问题的套路是, 我们按照从小到大的顺序把数组填在位置上, 这样类似扫描线的处理出逆序对个数
这道题我们也考虑这样做, 不难发现, 我们可以方便的统计放入数 \(i\) 之后, 产生的逆序对个数 \((\)在他之前放在他后面的会产生逆序对\()\) , 不难发现产生逆序对的个数具有单调性, 后面要用

如果我们能求出每次按序放完数字之后, 产生的逆序对个数, 那么就可以通过二分的方式确定答案, 非常的方便

假设考虑放了 \(i\) 个数字, 我们已经知道, 当前剩下的逆序对个数 \(ans\) 和当前在逆序对个数为 \(ans\) 的串中的排名 \(k\)
不难知道, 我们通过枚举剩下 \(n - i\) 个位置对应的排列的逆序对, 显然的, 剩下 \(n - i\) 个位置的逆序对个数越多, 当前位置的逆序对个数越少, 因此放的位置也更前, 可以确定是字典序更小的

所以本质上是处理出一堆排名之后的问题, 非常需要人类智慧

代码

#include <bits/stdc++.h>
using namespace std;
#define int __int128
#define lowbit(x) (x & (-x))
const int N = 1e6 + 5;
int n, fnl[N], s[N];
__int128 sum[300];
__int128 pre[300], k;
vector<vector<__int128>> dp;
__int128 read()
{
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int col[N];
void update(int pos, int w)
{
    for (int i = pos; i <= n; i += lowbit(i))
        s[i] += w;
}
int query(int pos)
{
    int res = 0;
    for (int i = pos; i > 0; i -= lowbit(i))
        res += s[i];
    return res;
}
void write(int x)
{
    if (x >= 10)
        write(x / 10);
    putchar(x % 10 + '0');
}
signed main()
{
    n = read(), k = read();
    dp.resize(n + 2);
    int w = 0;
    if (n <= 21)
        w = 220;
    else if (n <= 100)
        w = 100;
    else
        w = 10;
    for (int i = 0; i <= n; i++)
        dp[i].resize(w + 5);
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        pre[0] = dp[i - 1][0];
        for (int j = 1; j <= w; j++)
            pre[j] = pre[j - 1] + dp[i - 1][j];
        for (int j = 0; j < min(i, w); j++)
            dp[i][j] = pre[j];
        for (int j = i; j <= w; j++)
            dp[i][j] = pre[j] - pre[j - i];
    }
    int l = 0, r = 0, ans = 0;
    sum[0] = dp[n][0];
    for (ans = 0; ans <= w; ans++)
    {
        if (ans > 0)
            sum[ans] = sum[ans - 1] + dp[n][ans];
        if (sum[ans] >= k)
            break;
    }
    if (ans)
        k -= sum[ans - 1]; // 在逆序对数相同的排列中的排名
    for (int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for (int j = ans; j >= 0; j--)
        {
            tmp += dp[n - i][j];
            if (tmp >= k)
            {
                k -= tmp - dp[n - i][j];
                col[i] = ans - j + 1; // i 位置上逆序对的个数
                ans = j;
                break;
            }
        }
    }
    for (int i = 1; i <= n; i++)
        update(i, 1);
    for (int i = 1; i <= n; i++)
    {
        int l = 1, r = n, ans = 0;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (query(mid) < col[i])
                ans = mid, l = mid + 1;
            else
                r = mid - 1;
        }
        ans++;
        update(ans, -1);
        write(ans), putchar(' ');
    }
    return 0;
}

总结

递推思想的应用

posted @ 2024-12-02 20:50  Yorg  阅读(16)  评论(0)    收藏  举报