显然有决策单调性,但由于逆序对不容易计算,考虑分治DP。
solve(k,x,y,l,r)表示当前需要选k段,待更新的位置为[l,r],这些位置的可能决策点区间为[x,y]。暴力计算出(l+r)/2的决策位置s,两边递归下去继续操作。solve(k,x,s,l,mid-1),solve(k,s,y,mid+1,r)。
注意到每个位置每层只会被一个区间遍历到,加上树状数组在线更新逆序对的复杂度,总复杂度为$O(knlog^2n)$
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=40010,inf=1600000010; 7 int n,m,a[N],f[20][N],c[N],l,r,cur; 8 9 void add(int x,int k){ for (; x<=n; x+=x&-x) c[x]+=k; } 10 int que(int x){ int res=0; for (; x; x-=x&-x) res+=c[x]; return res; } 11 12 void upd(int L,int R){ 13 while (r<R) cur+=r-l+1-que(a[r+1]),add(a[++r],1); 14 while (l>L) cur+=que(a[l-1]),add(a[--l],1); 15 while (r>R) add(a[r--],-1),cur-=r-l+1-que(a[r+1]); 16 while (l<L) add(a[l++],-1),cur-=que(a[l-1]); 17 } 18 19 void solve(int k,int x,int y,int l,int r){ 20 if (l>r) return; 21 int mid=(l+r)>>1,id=min(mid-1,y); 22 f[k][mid]=inf; 23 for (int i=min(mid-1,y); i>=x; i--){ 24 upd(i+1,mid); 25 if (f[k-1][i]+cur<=f[k][mid]) f[k][mid]=f[k-1][i]+cur,id=i; 26 } 27 solve(k,x,id,l,mid-1); solve(k,id,y,mid+1,r); 28 } 29 30 int main(){ 31 freopen("bzoj5125.in","r",stdin); 32 freopen("bzoj5125.out","w",stdout); 33 scanf("%d%d",&n,&m); l=1; r=0; 34 rep(i,1,n) scanf("%d",&a[i]); 35 rep(i,1,n) f[0][i]=inf; 36 rep(j,1,m) solve(j,0,n-1,1,n); 37 printf("%d ",f[m][n]); 38 return 0; 39 }