题目大意:在填充一列数,使逆序对数最小。
有个结论,即当i<j且a[i]>a[j]时,将i,j调换一定更优。
所以填进去的数一定是不下降的。
然后树状数组一阵乱搞+dp就可以了。
调试笔记:树状数组边界要清楚。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int inf(1<<28);
int n,k,f[10010][110],num=0,a[10010],cost[10010][110],ans=0;
int tr[3][110];
int lowbit(int x){return x&(-x);}
void change(int root,int K,int c)
{
for(int i=K;i<=k;i+=lowbit(i))
tr[root][i]+=c;
}
int getsum(int root,int k)
{
int ans=0;
for(int i=k;i>=1;i-=lowbit(i))
ans+=tr[root][i];
return ans;
}
int get(int root,int l,int r)
{
if(l>r) return 0;
return getsum(root,r)-getsum(root,l-1);
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]!=-1) change(0,a[i],1),ans+=get(0,a[i]+1,k);
}
for(int i=1;i<=n;i++)
{
if(a[i]!=-1) change(0,a[i],-1),change(1,a[i],1);
else
{
num++;
for(int j=1;j<=k;j++) cost[num][j]=get(1,j+1,k)+getsum(0,j-1);
}
}
if(num==0){printf("%d",ans);return 0;}
for(int i=1;i<=num;i++) f[i][0]=inf;
for(int i=1;i<=num;i++)
for(int j=1;j<=k;j++)
f[i][j]=min(f[i][j-1],f[i-1][j]+cost[i][j]);
printf("%d",ans+f[num][k]);
}