[BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)

显然有决策单调性,但由于逆序对不容易计算,考虑分治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)$

 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 }

 

posted @ 2019-01-13 10:05  HocRiser  阅读(339)  评论(0编辑  收藏  举报