将长度为(n)的排列划分成(K)段,每段顺序对个数和最小
方程类似于邮局的式子
(f[i][j])表示前(i)个数分(j)段最小值,(w(i,j))表示区间([i,j])的顺序对个数
[f[i][j]=min_{0le k<i}{f[k][j-1]+w(k+1,j)}
]
不同的是邮局那道题可以直接预处理出(w),而这题不太好搞
树状数组暴力跳的话,可以用莫队思想将答案推至相邻区间
(O(n^2klog n)) (50\%) 好成绩
因为有(w(i,j+1)+w(i+1,j)< w(i,j)+w(i+1,j+1))
(画个图可知,扔到数轴上,发现无论如何都是严格单调的,并且可以和暴力拍上)
(w)满足四边形不等式具有决策单调性
然后枚举(j),设(g[i])为(f[i][j])的最优决策点,(f[i][j]=f[g[i]][j-1]+w[g[i]+1][i])
同样有(g[i-1]le g[i])
根据单调性,并且(w)无法快速求得,考虑分治维护
(solve(l,r,L,R))表示从([L,R])转移到([l,r]),令(mid=(l+r)/2)
暴力找到(mid)在([L,R])的决策点(x),根据单调性,([l,mid-1])用([L,x])转移,([mid+1,r])这段区间用([x,R])转移,暴力跳树状数组均摊复杂度(nlog n),一共分治(log)层,求解(k)次,(O(nklog^2n)) 可过
int f[N][26],a[N],c[N],n,K,sum,L,R,now;
inline int min(int a,int b){return a < b ? a : b;}
void add(int k,int x){for(;k <= n;k += k&-k) c[k] += x;}
int query(int x){
int res = 0;
for(;x;x-=x&-x) res += c[x];
return res;
}
void change(int l,int r){//暴力反复纵跳
while(L < l) sum -= query(a[L]-1),add(a[L++],-1);
while(L > l) sum += query(a[L-1]-1),add(a[--L],1);
while(R < r) sum += R-L+1 - query(a[R+1]),add(a[++R],1);
while(R > r) sum -= R-L+1 - query(a[R]),add(a[R--],-1);
}
void solve(int l,int r,int L,int R){
if(l > r)return;
int mid = l + r >> 1,x = L;
for(int i = L;i <= min(mid-1,R);i++){
change(i+1,mid);
int ans = f[i][now-1]+sum;
if(ans < f[mid][now])
f[mid][now] = ans,x = i;//寻找最优决策点
}
solve(l,mid-1,L,x); solve(mid+1,r,x,R);
}
int main(){
scanf("%d%d",&n,&K);
for(int i = 1;i <= n;++i) scanf("%d",&a[i]),a[i] = n-a[i]+1;
L = 1; /*左指针*/memset(f,0x3f,sizeof(f));
for(int i = 1;i <= n;++i)
change(1,i),f[i][1] = sum;
for(now = 2;now <= K;++now)
solve(1,n,1,n);
printf("%d",f[n][K]);
}