我们可以很容易的推断出$-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; }
原文地址:https://www.cnblogs.com/si-rui-yang/p/9907506.html
时间: 2024-10-10 15:39:51