[DP] BZOJ1831: [AHOI2008]逆序对

题意

给出一个n个数的数列,每个数都是-1或是一个在1~K之间的数。-1表示这个位置可以填任意的数字。
求最少能有多少个逆序对。
(n<=10000 K<=100)

题解

bzoj双倍经验题 1831=1786
首先需要得到一个显然的结论,对于一个数列,如果我们交换一对逆序的元素,总逆序对数一点小于之前的总数。所以我们填的-1一定是不降的。
所以-1之间不会产生任何代价。
Onklog2n 预处理出每个-1填不同数时产生的逆序对数。
然后设 f[i][j] 表示前i个-1,其中第i个天j的最小代价,然后 O(nK) 瞎DP即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10005, maxw=105;
int n,m,K,ans,ans2,a[maxn],w[maxn][maxw],f[maxn][maxw],bit[2][maxw];
void Updata(int k,int x,int val){
    for(;x<=K;x+=(x&(-x))) bit[k][x]+=val;
}
int Query(int k,int x){
    int res=0;
    for(;x;x-=(x&(-x))) res+=bit[k][x];
    return res;
}
int main(){
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]); 
        if(a[i]!=-1) ans2+=Query(1,K)-Query(1,a[i]), 
        Updata(1,a[i],1);
    }
    for(int i=1;i<=n;i++) if(a[i]==-1){
        m++;
        for(int j=1;j<=K;j++) w[m][j]=Query(0,K)-Query(0,j) + Query(1,j-1);
    } else Updata(1,a[i],-1), Updata(0,a[i],1);
    memset(f,63,sizeof(f)); f[0][1]=0;
    for(int i=1;i<=m;i++) 
     for(int j=1;j<=K;j++)
      for(int k=1;k<=j;k++) f[i][j]=min(f[i][j],f[i-1][k]+w[i][j]);
    ans=1e+9;
    for(int i=1;i<=K;i++) ans=min(ans,f[m][i]);
    printf("%d\n",ans+ans2);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值