题解:[ARC126D] Pure Straight

题目传送门

题意分析

观察数据范围,发现 \(k\leq16\),可以考虑指数级做法。

\(f_{i,s}\) 表示处理到 \(a_i\),得到的升序序列的元素集合为 \(s\) 的最小操作数。如果 \(f_{i,s}\) 不可能成立,钦定 \(f_{i,s}=+\infty\)

边界显然有 \(f_{0,\varnothing}=0\),考虑 \(f_{i,s}\) 的转移:

  • 选择 \(a_i\)

    此时要求 \(a_i\in s\)

    \(t=s\setminus\set{a_i}\),加入 \(a_i\) 就要把 \(t\) 中大于等于 \(a_i\) 的都交换到后面去,那么

    \[f_{i,s}\leftarrow\min(f_{i,s},f_{i-1,t}+\vert\set{x\in t\mid x>a_i}\vert) \]

    实际维护上,可以用 t&~((1<<a[i])-1) 来表示 \(\set{x\in t\mid x\geq a_i}\)。因为 \(a_i\not\in t\),带不带等号无所谓。

  • 不选 \(a_i\),此时对 \(s\) 没有要求。

    则最终 \(a_i\) 在连续段左边或者右边,往左和往右取 \(\min\) 即可,有

    \[f_{i,s}\leftarrow\min(f_{i,s},f_{i-1,s}+\min(\vert s\vert,k-\vert s\vert)) \]

答案即 \(f_{n,\set{1,2,3,\cdots,k}}\)

AC 代码

//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=200,K=16,inf=0x3f3f3f3f;
int n,k,a[N+1],f[N+1][1<<K|1];
int popcount(int s){
	return __builtin_popcount(s);
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	memset(f,0x3f,sizeof(f));
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]--;
	}
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int s=0;s<(1<<k);s++){
			if(s>>a[i]&1){
				int t=s^(1<<a[i]);
				f[i][s]=min(f[i][s],f[i-1][t]+popcount(t&~((1<<a[i])-1)));
			}
			f[i][s]=min(f[i][s],f[i-1][s]+min(popcount(s),k-popcount(s)));
		}
	}
	cout<<f[n][(1<<k)-1]<<'\n';
	
	cout.flush(); 
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-08-08 16:45  TH911  阅读(2)  评论(0)    收藏  举报