1. 程式人生 > >[BZOJ5125]小Q的書架(決策單調性+分治DP+樹狀陣列)

[BZOJ5125]小Q的書架(決策單調性+分治DP+樹狀陣列)

顯然有決策單調性,但由於逆序對不容易計算,考慮分治DP。

solve(k,x,y,l,r)表示當前需要選j段,待更新的位置為[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)$

 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\n",f[m][n]); 38 return 0; 39 }