【题解】邦邦的大合唱站队
\(\text{Solution:}\)
设计状态 \(f[i]\) 表示在状态\(i\)拼齐的情况下需要的最小出队人数。
那么如果要拼进来一个 \(j\) 那么,就需要把 \(j\) 乐队所有人都拿出来。
但是,处于原本就需要拼的位置的人不用,这部分的人是从 \(statePre\) 到 \(stateNow\) 这段长度的差中区间的 \(j\) 的数目。
可以预处理啥的,于是就可以 \(O(m\cdot 2^m)\) \(DP\) 了。
#include<bits/stdc++.h>
using namespace std;
const int dyx=(1<<30);
int n,m,a[200010];
int f[1<<21],sum[1<<21];
int cnt[21],p[21][200010];
inline int Min(int x,int y){return x<y?x:y;}
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i],cnt[a[i]-1]++;
for(int i=0;i<m;++i)sum[1<<i]=cnt[i];
for(int i=1;i<=n;++i){
for(int j=0;j<m;++j)p[j][i]=p[j][i-1];
p[a[i]-1][i]++;
}
for(int i=0;i<(1<<m);++i)
for(int j=0;j<m;++j){
if(i>>j&1)continue;
sum[i|(1<<j)]=sum[i]+cnt[j];
}
f[0]=0;
for(int i=1;i<(1<<m);++i){
f[i]=dyx;
for(int j=0;j<m;++j){
if(i>>j&1){
f[i]=Min(f[i],f[i^(1<<j)]+cnt[j]-p[j][sum[i]]+p[j][sum[i]-cnt[j]]);
}
}
}
printf("%d\n",f[(1<<m)-1]);
return 0;
}