CodeForces 1730F Almost Sorted

发布时间 2023-09-20 11:27:13作者: zltzlt

洛谷传送门

CF 传送门

过程相当于是将 \(p\) 重排列。设 \(b_i\)\(p\) 中数为 \(i\) 的位置。考虑当前填的数 \(x\) 贡献的逆序对数,相当于是当前所有已经填入 \(p\) 的数 \(y\),都有 \([b_y > b_x]\) 的贡献。

考虑 \(p_{q_i} \le p_{q_j} + k\) 意味着什么。

当正在填 \(p_{q_i}\) 时,之后还没填的数 \(\ge p_{q_i} - k\);当正在填 \(p_{q_j}\) 时,之前填了的数 \(\le p_{q_j} + k\)

综合一下就是,若考虑某个前缀已经填入 \(p\) 的状态,最小的还没填的数 \(\le\) 最大的已经填了的数 \(+ k\)

由此可以想到状压。设 \(f_{i, S}\) 为当前最小的还没填的数为 \(i + 1\)\([i + 1, i + k + 1]\) 的数的填入状态为 \(S\)(填入为 \(1\),没填入为 \(0\))。

转移枚举当前位填 \(x\)\(x\) 只能在 \([i + 1, i + k + 1]\) 中),分开计算 \([1, i]\) 的贡献和 \([i + 1, i + k + 1]\) 的贡献即可。前部分预处理一个前缀有多少个 \(b_i \ge j\),后部分暴力。然后还要计算,填了 \(x\) 后,新的最小的没填的数是多少。

时间复杂度 \(O(n^2 + n k^2 2^k)\)

压行后是 CF 最短解。

code
// Problem: F. Almost Sorted
// Contest: Codeforces - Codeforces Round 823 (Div. 2)
// URL: https://codeforces.com/problemset/problem/1730/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 5050;
const int maxm = (1 << 9) + 9;

int n, m, a[maxn], b[maxn], f[maxn][maxm], c[maxn][maxn];

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		b[a[i]] = i;
	}
	mems(f, 0x3f);
	f[0][0] = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			c[i][j] = c[i - 1][j] + (j <= b[i]);
		}
	}
	for (int i = 0; i < n; ++i) {
		for (int S = 0; S < (1 << (m + 1)); ++S) {
			for (int j = 0; j <= m; ++j) {
				if (S & (1 << j)) {
					continue;
				}
				int t = c[i][b[i + j + 1]];
				for (int k = 0; k <= m; ++k) {
					if (S & (1 << k)) {
						t += (b[i + k + 1] > b[i + j + 1]);
					}
				}
				int T = S | (1 << j), k = 0;
				while (k <= m && (T & (1 << k))) {
					++k;
				}
				T >>= k;
				f[i + k][T] = min(f[i + k][T], f[i][S] + t);
			}
		}
	}
	printf("%d\n", f[n][0]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}