[AHOI2008] 逆序对

link

我们可以很容易的推断出$-1$是单调不降的,若$i>j$且$a_i$与$a_j$都没有填数,若填完之后$a_i>a_j$或者$a_i<a_j$,则对答案产生影响的只在$[i,j]$之间,则$a_i<a_j$对答案产生的贡献更小,则其实每个不同位置的$-1$其实是互不影响的,所以就可以用$dp$实现

设$dp(i,j)$表示这是从右往左数第$i$个$-1$,这里填j的最小逆序对数(这里的逆序对是只与$-1$有关的,其他的单算)

则$dp(i,j)=min(dp(i-1,p)+在第i个-1左面不是-1的对此数新产生的逆序对数+此数填后对右面产生的贡献) (j \leq p)$

我们可以用线段树维护逆序对,时间复杂度:$O(n\times k^2)$

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read(){
    int f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
int n,k,a[10001],cnt[10001],ans[40014];
void add(int k,int l,int r,int x,int y){
    if(x>y) return ;
    if(x<=l&&r<=y){ans[k]++;return;}
    int mid=l+r>>1;
    if(x<=mid) add(k<<1,l,mid,x,y);
    if(mid<y) add(k<<1|1,mid+1,r,x,y);
    ans[k]=ans[k<<1]+ans[k<<1|1];
    return;
}
int query(int k,int l,int r,int x,int y){
    if(x>y) return 0;
    if(x<=l&&r<=y) return ans[k];
    int mid=l+r>>1,res=0;
    if(x<=mid) res+=query(k<<1,l,mid,x,y);
    if(mid<y) res+=query(k<<1|1,mid+1,r,x,y);
    return res;
}
int cost[10001][101],sum,dp[10001][101],tot,minn,inf=2<<30-1;
int main(){
    minn=inf;
    memset(dp,127/3,sizeof(dp));
    n=read(),k=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        if(a[i]==-1)
            cnt[++cnt[0]]=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=k;j++){
            cost[i][j]=cost[i-1][j];
            if(j<=a[i]) cost[i][j]++;
        }
    }
    for(int i=1;i<=k;i++) dp[0][i]=0;
    for(int i=n;i>=1;i--){
        if(a[i]!=-1){
            sum+=query(1,1,k,1,a[i]-1);
            add(1,1,k,a[i],a[i]);
        }else{
            tot++;
            for(int j=1;j<=k;j++){
                for(int p=j;p<=k;p++){
                    dp[tot][j]=min(dp[tot-1][p]+query(1,1,k,1,j-1)+cost[i][j+1],dp[tot][j]);
                    if(tot==cnt[0]) minn=min(minn,dp[tot][j]);
                }
            }
        }
    }
    if(minn==inf) cout<<sum;
    else cout<<sum+minn;
}
View Code

 

posted @ 2018-11-05 09:45  siruiyang_sry  阅读(211)  评论(0编辑  收藏  举报