P5574
将长度为\(n\)的排列划分成\(K\)段,每段顺序对个数和最小
方程类似于邮局的式子
\(f[i][j]\)表示前\(i\)个数分\(j\)段最小值,\(w(i,j)\)表示区间\([i,j]\)的顺序对个数
\[f[i][j]=\min_{0\le k<i}\{f[k][j-1]+w(k+1,j)\}
\]
不同的是邮局那道题可以直接预处理出\(w\),而这题不太好搞
树状数组暴力跳的话,可以用莫队思想将答案推至相邻区间
\(O(n^2k\log 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]\)转移,暴力跳树状数组均摊复杂度\(n\log n\),一共分治\(\log\)层,求解\(k\)次,\(O(nk\log^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]);
}