题解:SP64 PERMUT1 - Permutations

题目传送门

题目翻译

给定一个 \(d\),接下来 \(d\) 行,每行一个 \(n\)\(k\) 查找这个序列的全排列中一共有多少个排列含有 \(k\) 个逆序对。

思路

动态规划。

\(f_{i,j}\) 表示 \(1\)\(i\) 中存在 \(j\) 个逆序对,所以方程为:

\[f_{i,j}= \sum ^j _{k=j-\min(i-1,j)} f_{i-1,k}= \sum ^j _{k=0} f_{i-1,k} - \sum ^{j-\min(i-1,j)} _{k=0} f_{i-1,k} \]

Q: 可这复杂度也太高了吧。

A: 别怕,可以用前缀和来优化。

优化详见注释。

CODE

#include <bits/stdc++.h>
using namespace std;
long long f[10010], g[2][10010];
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int d;
    cin >> d;
    while (d--) {
        int n, k;
        cin >> n >> k;
        memset(f, 0, sizeof(f)); // 清空数组。
        memset(g, 0, sizeof(g)); // 同上。
        f[0] = 1; // 边界。
        for (int i = 0; i <= k; i++)
            g[1][i] = ((i == 0) ? 0 : g[1][i - 1]) + f[i]; // 前缀和。
        for (int i = 2; i <= n; i++)
            for (int j = 0; j <= k; j++) // 方程。
            {
                f[j] = g[(i - 1) & 1][j] - ((j - min(i - 1, j) - 1 < 0) ? 0ll : g[(i - 1) & 1][j - min(i - 1, j) - 1]);
                g[i & 1][j] = ((j == 0) ? 0 : g[i & 1][j - 1]) + f[j];
            }
        cout << f[k] << "\n"; // 输出答案。
    }
    return 0; // 结束。
}

彩蛋

本题是多倍经验 P6323 [COCI2006-2007#4] ZBRKAP1521 求逆序对P2513 [HAOI2009] 逆序对数列

posted @ 2026-05-24 13:37  Synthx  阅读(3)  评论(0)    收藏  举报