<!--more-->ios
在咱們瞭解的DP方程中,常常會有$f[i]=sum_{max}/sum_{min}/min/max{f[j]+calc(i,j)}$,而且calc(i,j)知足四邊形不等式,這種方程存在,而一般狀況下,calc(i,j)能夠很是輕鬆的得出,好比說$x^q$,或者sum[i][j],又或者是什麼其餘的東西。可是,有些時候,咱們並不能在$O(logn)/O(1)$的時間內得出,這種狀況下,正常經過維護單調隊列而且二分的方法就沒辦法完成了。數組
這種狀況下,咱們就須要考慮calc(i,j)的求法了,這種時候,若是咱們發現calc(i,j)能夠經過分治來均攤$O(nlogn)$處理出來的時候,咱們就能夠經過總體二分來維護決策單調性來轉移,保證總體時間複雜度的正確性。優化
這道題很顯然,咱們要將序列分紅$K$段使得這$K$段的逆序對和最小。spa
正常DP方程,f[i][j]=f[i-1][k]+calc(k+1,j);blog
咱們發現calc(k+1,j)知足$calc(i,j)+calc(i-1,j+1) \ge calc(i,j+1)+calc(i+1,j)$因此這個具備決策單調性,可是,咱們雖然將DP方程的時間複雜度減小到$O(nklogn)$可是,咱們發現,每次計算calc(i,j)仍是$O(n)$的,因此總時間複雜度不變,所以,咱們考慮用分治優化決策單調性。隊列
咱們考慮在總體二分的過程當中,calc(i,j)每一層的時間複雜度爲$O(n)$,而一共$logn$層,因此時間複雜度爲$O(nlogn)$,這樣,在DP的同時,calc(i,j)的時間複雜度也下降了,而爲了維護calc(i,j)須要用到樹狀數組求逆序對,因此總時間複雜度爲$O(nklog^2n)$顯然,是能夠作的...get
若是K出的大一點的話,我以爲能夠帶權二分...因此時間複雜度能夠達到$O(nlogklog^2n)$,因此還能夠增強增強,嗯,就是下一道考試題了!string
附上代碼:it
#include <cstdio> #include <algorithm> #include <cstring> #include <cstdlib> #include <iostream> #include <cmath> #include <queue> #include <bitset> using namespace std; #define N 40005 int f[N],sum[N],g[N],n,k,rl,rr,ret,a[N]; void fix(int x,int v){for(;x<N;x+=x&-x)sum[x]+=v;} int find(int x){int re=0;for(;x;x-=x&-x)re+=sum[x];return re;} void get(int l,int r) { while(rl<l)fix(a[rl],-1),ret-=find(a[rl++]-1); while(rl>l)fix(a[--rl],1),ret+=find(a[rl]-1); while(rr<r)fix(a[++rr],1),ret+=find(n)-find(a[rr]); while(rr>r)fix(a[rr],-1),ret-=find(n)-find(a[rr--]); } void solve(int l,int r,int L,int R) { if(l>r)return ;int m=(l+r)>>1,p; for(int i=min(m-1,R);i>=L;i--) { get(i+1,m); if(g[i]+ret<f[m])f[m]=g[i]+ret,p=i; }solve(l,m-1,L,p);solve(m+1,r,p,R); } int main() { scanf("%d%d",&n,&k);rr=n,rl=1; for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[i]=f[i-1]+find(n)-find(a[i]),fix(a[i],1);ret=f[n]; for(int i=2;i<=k;i++) { memcpy(g,f,sizeof(f[0])*(n+1)); memset(f,0x3f,sizeof(f[0])*(n+1)); solve(1,n,1,n); }printf("%d\n",f[n]); }