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;
}
posted @ 2025-10-19 10:55  lucasincyber  阅读(6)  评论(0)    收藏  举报