[DP] BZOJ1831: [AHOI2008]逆序对

来源:互联网 发布:淘宝卖家运费险设置 编辑:程序博客网 时间:2024/05/28 18:43

题意

给出一个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 0
原创粉丝点击