Luogu P3672 小清新签到题

题目描述

https://www.luogu.com.cn/problem/P3672

Solution

我们用 $f[i][j]$ 表示后 $i$ 位已经填完,现在逆序对有 $j$ 个

这个东西可以做到 $O(n^3)$,注意在 $dp$ 的时候,$f[i][j]$ 要和 $k+1$ 取 $min$

然后对于第 $k$ 小,我们每一位都从小到大枚举即可,时间复杂度同样为 $O(n^3)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 310
#define maxm 300 * 299 / 2 + 10
#define ll long long
using namespace std;

int n, m;
ll k;

ll f[maxn][maxm];
bool use[maxn];

int ans[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> k >> m;
f[n + 1][0] = 1;
for (int i = n + 1; i > 1; --i) {
for (int j = 0; j <= m; ++j) {
int l = j, r = min(j + n - i + 1, m);
f[i - 1][l] += f[i][j];
f[i - 1][r + 1] -= f[i][j];
}
for (int j = 1; j <= m; ++j) f[i - 1][j] += f[i - 1][j - 1];
for (int j = 0; j <= m; ++j) f[i - 1][j] = min(f[i - 1][j], k + 1);
}
for (int i = 1; i <= n; ++i) {
ll s = 0;
for (int j = 1; j <= n; ++j) {
if (use[j]) continue;
int c = 0;
for (int k = 1; k < j; ++k) c += !use[k];
if (f[i + 1][m - c] + s >= k) { ans[i] = j; use[j] = 1; m -= c; k -= s; break; }
s += f[i + 1][m - c];
}
}
for (int i = 1; i <= n; ++i) cout << ans[i] << " ";
return 0;
}