考虑 $dp$ ,发现之前的 $-1$ 可能会产生贡献不好处理
贪心一下发现每个位置填的数必须单调不减,所以就不用考虑之前填的数
设 $f[i][j]$ 表示当前考虑到第 $i$ 个位置,填的数为 $j$ 时的最小代价
那么有 $f[i][j]=f[pre][k]+val[i][j],k<=j$,$pre$ 是上一个 $-1$ 的位置,$val[i][j]$ 是在位置 $i$ 填 $j$ 时的代价,这个可以用树状数组求出
转移只要维护一个前缀最小值即可做到 $O(1)$,记得答案要统计本身的逆序对数
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e4+7,M=207; int n,K,a[N]; ll f[N][M],mi[N][M],ans; int t[2][N]; inline void add(int x,int v,int p) { while(x<=K) t[p][x]+=v,x+=x&-x; } inline int ask(int x,int p) { int res=0; while(x) res+=t[p][x],x-=x&-x; return res; } int main() { n=read(),K=read(); for(int i=1;i<=n;i++) a[i]=read(); memset(f,0x3f,sizeof(f)); memset(mi,0x3f,sizeof(mi)); int pre=0,tot=0; for(int i=1;i<=K;i++) mi[0][i]=0; for(int i=1;i<=n;i++) if(a[i]!=-1) add(a[i],1,1); for(int i=1;i<=n;i++) { if(a[i]!=-1) { ans+=(tot-ask(a[i],0)); add(a[i],1,0); add(a[i],-1,1); tot++; continue; } for(int j=1;j<=K;j++) f[i][j]=mi[pre][j] + (tot-ask(j,0)) + ask(j-1,1); for(int j=1;j<=K;j++) mi[i][j]=min(mi[i][j-1],f[i][j]); pre=i; } printf("%lld\n",ans+mi[pre][K]); return 0; }