CF2145D Inversion Value of a Permutation 题解
思路
我们把逆序值转化为非逆序值,即没有逆序对的数对 \((i,j)\)(\(i<j\))的数量。数学定义即为:若 \((i,j)\) 是没有逆序对的数对,则:
\[\forall 1 \le i < j \le n,\;a_i < a_j
\]
我们会发现如果 \([i,j]\) 没有逆序对,则 \([i,j]\) 严格递增。同时,我们会发现,非逆序值 $= \displaystyle \frac{n \times (n-1)}{2} - $ 逆序值,并且一段长度为 \(k\) 的递增子段会增加 \(\displaystyle \frac{k \times (k-1)}{2}\) 的非逆序值。
我们考虑用 dp 预处理每一种状态。设 \(dp_{i,j}\) 代表能否成长度为 \(i\),且非逆序值为 \(j\) 的 \(1\) 至 \(i\) 的排列。直接暴力枚举 \(k\) 代表从 \(i\) 往后加的递增字段的长度,易得:
\[dp_{i+k,j+\frac{k\times(k-1)}{2}} = dp_{i,j}
\]
其中 \(i+k\le 30\) 即可。这样就可以的到每一种状态能否到达。用一个递归函数求解答案即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 35, M = 905;
int t, n, k, cur;
int dp[N][M], ans[N];
void work(int n, int k)
{
if (!n) return;
for (int i = 1; i <= n; i++)
{
if (k >= i * (i - 1) / 2 && dp[n - i][k - i * (i - 1) / 2])
{
for (int j = 1; j <= i; j++)
ans[n - i + j] = ++cur;
work(n - i, k - i * (i - 1) / 2);
return;
}
}
}
void solve()
{
scanf("%d%d", &n, &k);
k = n * (n - 1) / 2 - k;
if (!dp[n][k]) printf("0\n");
else
{
cur = 0;
work(n, k);
for (int i = 1; i <= n; i++)
printf("%d ", ans[i]);
printf("\n");
}
}
void init()
{
dp[0][0] = 1;
for (int i = 0; i < 30; i++)
for (int j = 0; j <= i * (i - 1) / 2; j++)
if (dp[i][j])
for (int k = 1; i + k <= 30; k++)
dp[i + k][j + k * (k - 1) / 2] = 1;
return;
}
int main()
{
init();
scanf("%d", &t);
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号