社论 22.10.15 排列逆序对计数

之前对着第二维就是求和生成然后什么玩意都搞不出来 wssb


给定 \(n,k\),求长度为 \(n\) 、逆序对数为 \(k\) 的排列数量模 \(998244353\) 后的值。

\(n,k \le 3000\)

考虑令 \(f_{i,j}\) 为长度为 \(i\),逆序对数为 \(j\) 的排列的数量。答案即为 \(f_{n,k}\)

考虑通过插入计数。
我们首先有一个长度为 \(i\),逆序对数为 \(j\) 的排列。现在向其中插入元素 \(i+1\)。容易发现其插入在从前往后数的第 \(k\) 位置会导致其后面的 \((i-k+1)\) 个元素和它生成逆序对,因此 \(f_{i,j}\) 能贡献给 \(f_{i+1,j+i-k+1} \text{ s.t. } 0\le k \le i\)
重写一下,我们有 \(dp\) 式:

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

然后能做到 \(O(n^2 k)\) 的复杂度。

我们发现后面的部分其实是一个前缀和差分的形式,因此维护一下前缀和能做到 \(O(nk\log n) \sim O(nk)\) 的复杂度。

O(nk log n)
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
int n, m, x;

struct BIT {
    int Index[3005];
    int operator [] (int p) {
        int ret = 0;
        while (p) {
            ret += Index[p];
            p ^= p & -p;
        } return ret;
    }
    void add(int p, int v) {
        while (p <= n) {
            Index[p] += v;
            p += p & -p;
        }
    }
} f[3005];

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    f[1].add(1, 1);
    rep(i,2,n) {
        rep(j,0,min((i * (i-1)) >> 1, n)) {
            f[i].add(j + 1, (f[i-1][j + 1] - f[i-1][j - min(i-1,j)]));
        }
    } rep(i,1,m) {
        cin >> x; x ++;
        cout << (f[n][x] - f[n][x-1]) << '\n';
    }
}



[能不能再给力一点啊?]

题面相同。

\(n,k \le 10^5\)

认为 \(n,k\) 同阶,因此下文中渐进式内只使用 \(n\)

考虑对第二维求和得到生成函数

\[F_i(x) = \sum_{j=1}^{\infty} f_{i,j} \ x^j \]

则答案即为 \([x^k]F_n(x)\)

我们考虑转移方程。我们发现,\(j\) 位置能够从 \(j-1\sim j-i+1\) 转移过来。放在生成函数上,\(F_i(x)\) 可以通过 \(F_{i-1}(x)\sim x^{i-1}\times F_{i-1}(x)\) 转移过来。
于是有

\[F_n(x) =\left( \sum_{i=0}^{n-1} x^i\right) F_{n-1} (x) = \left( \frac {1-x^n}{1-x} \right) F_{n-1} (x) = \prod_{i=1}^n \frac {1-x^i}{1-x} = \left( \prod_{i=1}^n 1-x^i \right) (1-x)^{-n} \]

考虑 \(f(x) = \prod_{i=1}^n (1-x^i)\)\(g(x) = (1-x)^{-n}\) 的求法。

\(f(x)\) 是五边形数。有关五边形数:

五边形数定理 \(\text{ (Euler)}\)

\(\Phi(x)\) 为欧拉函数的生成函数。有下式:

\[\Phi(x) = \prod_{i=1}^n (1-x^i) = \sum_{i=0}^{\infty} (-1)^i x^{\frac{i(3i\pm 1)}{2}} \]

我们发现 \(f(x)\) 的幂级数形式只有 \(O(\sqrt n)\) 项有值。直接生成即可。这部分的复杂度为 \(O(\sqrt n)\)

\(g(x)\) 可以直接通过多项式快速幂在 \(O(n\log n)\) 的复杂度内生成。

生成后直接做多项式卷积即可。复杂度 \(O(n \log n)\)



[能不能再给力一点啊?]

题面相同。

\(k \le 10^5, n\le 10^{18}\)

注意到在上文中我们只需要多项式中第 \(k\) 次项。因此直接从 \(k\) 位置截断即可得到最终答案。

随后我们可以在 \(O(\sqrt k)\) 的时间内生成 \(f\)
随后我们可以使用多项式快速幂得到 \(g\)

时间复杂度为 \(O(k\log k)\)



使用生成函数方式证明 \(f\) 的行取值对称。

\[\begin{aligned} F(\frac 1x) &= \prod_{i=1}^n \frac{1 - \left(\frac 1x\right)^i}{1-\frac 1x} \\ &=\ \prod_{i=1}^n \frac{1 - \frac 1{x^i}}{1-\frac 1x} \\ &=\ \prod_{i=1}^n \frac{x^i-1}{x^i}\frac{x}{x-1} \\ &=\ \prod_{i=1}^n \frac{x^i-1}{x^{i-1} (x-1)} \end{aligned}\]

于是有

\[\frac{F(x)}{F(\frac 1x)} = \prod_{i=1}^n \frac {1-x^i}{1-x} \frac{x^{i-1} (x-1)}{x^i-1} = \prod_{i=1}^n x^{i-1} = x^{\frac {n(n-1)}2} \]

于是有

\[\begin{aligned} [x^k]F(x) &= [x^k]F(\frac 1x)x^{\frac {n(n-1)}2} \\ &=\ [x^{k-\frac {n(n-1)}2}]F(\frac 1x) \\ &=\ [x^{\frac {n(n-1)}2-k}]F(x) \end{aligned}\]

由于 \(F(x)\) 有意义的取值部分位于 \(x^0 \sim x^\frac {n(n-1)}2\) 部分,因此我们证明了 \(f_{n,i}\) 的取值的对称性。



关于“能不能再给力一点啊?”这件事,由于我多项式远处系数提取的功底不够扎实,先咕着。

posted @ 2022-10-15 20:44  joke3579  阅读(108)  评论(5编辑  收藏  举报