![[BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组) [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
显然有决策单调性,但由于逆序对不容易计算,考虑分治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(kn\log^2n)$
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=,inf=;
int n,m,a[N],f[][N],c[N],l,r,cur; void add(int x,int k){ for (; x<=n; x+=x&-x) c[x]+=k; }
int que(int x){ int res=; for (; x; x-=x&-x) res+=c[x]; return res; } void upd(int L,int R){
while (r<R) cur+=r-l+-que(a[r+]),add(a[++r],);
while (l>L) cur+=que(a[l-]),add(a[--l],);
while (r>R) add(a[r--],-),cur-=r-l+-que(a[r+]);
while (l<L) add(a[l++],-),cur-=que(a[l-]);
} void solve(int k,int x,int y,int l,int r){
if (l>r) return;
int mid=(l+r)>>,id=min(mid-,y);
f[k][mid]=inf;
for (int i=min(mid-,y); i>=x; i--){
upd(i+,mid);
if (f[k-][i]+cur<=f[k][mid]) f[k][mid]=f[k-][i]+cur,id=i;
}
solve(k,x,id,l,mid-); solve(k,id,y,mid+,r);
} int main(){
freopen("bzoj5125.in","r",stdin);
freopen("bzoj5125.out","w",stdout);
scanf("%d%d",&n,&m); l=; r=;
rep(i,,n) scanf("%d",&a[i]);
rep(i,,n) f[][i]=inf;
rep(j,,m) solve(j,,n-,,n);
printf("%d\n",f[m][n]);
return ;
}