CF1730F Almost Sorted (状压 dp)

CF1730F Almost Sorted

状压 dp

题目的描述有点奇怪,实际上就是将 p 在满足要求的情况下重排列,求下标的逆序对最小值。

根据条件,我们猜测前面的数都不会很大,于是考虑从左到右插入值,若当前插入的值为 ai,那么由限制条件可知,前面放的数都 ai+k,同时 ai 的部分一定已经放完了(否则放后面不满足条件)。所以在任何时刻,若当前最右端的值为 ai,那么一定使用了 [1,ai],可能使用过 [ai+1,ai+k+1]

考虑 dp。[1,ai] 的部分作为阶段,状压 [ai+1,ai+k+1] 的部分,所以设 fi,s 表示当前已经放完了 [1,ai][ai+1,ai+k+1] 的使用情况为 s,下标的最小逆序对数。

转移考虑枚举下一个要放的数,并处理出对应增加的逆序对数,[1,ai] 的部分可以转移的时候预处理,状态 s 的部分直接暴力枚举累加即可。

复杂度 O(n2+n2kk2)

PS:可以用 BIT 将 n2 优化成 nlogn

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 5e3 + 10, K = 9;
int n, k, lim;
int pos[N], sum[N];
int f[N][1 << K];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> k;
	k++;
	for(int i = 1; i <= n; i++) {
		int p;
		std::cin >> p;
		pos[p] = i;
	}
	lim = (1 << k) - 1;
	memset(f, 0x3f, sizeof(f));
	f[0][0] = 0;
	for(int i = 0; i <= n; i++) {
		if(i) {
			for(int j = 1; j < pos[i]; j++) sum[j]++;
		}
		for(int s = 0; s <= lim; s++) {
			if(f[i][s] == iinf) continue;
			for(int j = 1; j <= k && i + j <= n; j++) {
				if(!(s >> (j - 1) & 1)) {
					int p = i, t = s | (1 << (j - 1)), val = sum[pos[i + j]];
					while(t & 1) {
						p++;
						t >>= 1;
					}
					for(int x = 1; x <= k && i + x <= n; x++) if((s >> (x - 1) & 1) && pos[i + x] > pos[i + j]) val++;
					f[p][t] = std::min(f[p][t], f[i][s] + val);  
				}
			}
		}
	}
	std::cout << f[n][0] << "\n";
	return 0;
}
posted @   Fire_Raku  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示