1. 程式人生 > >BZOJ5125 小Q的書架(決策單調性+動態規劃+分治+樹狀數組)

BZOJ5125 小Q的書架(決策單調性+動態規劃+分治+樹狀數組)

zoj esp 基礎 out 決策單調 註意 spa void get

  設f[i][j]為前i個劃成j段的最小代價,枚舉上個劃分點轉移。容易想到這個dp有決策單調性,感性證明一下比較顯然。如果用單調棧維護決策就不太能快速的求出逆序對個數了,改為使用分治,移動端點時樹狀數組維護即可,類似莫隊的每次都在原有基礎上更新。註意更新時先加再減。感覺復雜度非常玄學絲毫不能看出為啥只需要更新nlog次?

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using
namespace std; #define ll long long #define N 40010 #define M 11 #define inf 1600000010 char getc(){char c=getchar();while ((c<A||c>Z)&&(c<a||c>z)&&(c<0||c>9)) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar();
while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,a[N],f[M][N],tree[N],l,r,cur; void add(int k,int x){while (k<=n) tree[k]+=x,k+=k&-k;} int query(int k){int s=0;while (k) s+=tree[k],k-=k&-k;return
s;} void update(int L,int R) { while (r<R) cur+=r-l+1-query(a[r+1]),add(a[++r],1); while (l>L) cur+=query(a[l-1]),add(a[--l],1); while (r>R) add(a[r--],-1),cur-=r-l+1-query(a[r+1]); while (l<L) add(a[l++],-1),cur-=query(a[l-1]); } void solve(int k,int x,int y,int l,int r) { if (l>r) return; int mid=l+r>>1,id=min(mid-1,y);f[k][mid]=inf; for (int i=min(mid-1,y);i>=x;i--) { update(i+1,mid); if (f[k-1][i]+cur<=f[k][mid]) f[k][mid]=f[k-1][i]+cur,id=i; } solve(k,x,id,l,mid-1); solve(k,id,y,mid+1,r); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5125.in","r",stdin); freopen("bzoj5125.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read();l=1,r=0; for (int i=1;i<=n;i++) a[i]=read(); f[0][0]=0;for (int i=1;i<=n;i++) f[0][i]=inf; for (int j=1;j<=m;j++) solve(j,0,n-1,1,n); cout<<f[m][n]; return 0; }

BZOJ5125 小Q的書架(決策單調性+動態規劃+分治+樹狀數組)