思路:
设表示前个数分成段的最小花费,那么显然有
一个满足决策单调性的充要条件是:对于两个决策点,若在处优于,则在处都优于。
定理:若满足四边形不等式,则满足决策单调性。
定义:若二元函数满足,则称其满足四边形不等式。
推论,若只与有关,即,则必然符合四边形不等式。且此时,该类适宜用单调队列优化。
而这题是满足的,若在都出现了,则不等式右边比左边多出的贡献,其它情况一样。假设区间的中点为、决策区间为,那么可以先求出,并得到最优决策点;接着递归处理
注意到每次递归处理时决策区间的左端点是递增的,所以我们有了莫队的思路,而左端点递增保证了其一层的移动指针复杂度是线性的。暴力更新值的复杂度平摊下来是。
MyCode:
#include<bits/stdc++.h> using namespace std; const int maxn=4e4; int f[maxn][105],last[maxn],nex[maxn],pre[maxn],a[maxn]; int now,pl=1,pr,sum; int cost(int l,int r) { while(pl<l) { if(nex[pl]<=pr) sum-=nex[pl]-pl; pl+=1; } while(pl>l) { pl-=1; if(nex[pl]<=pr) sum+=nex[pl]-pl; } while(pr>r) { if(pre[pr]>=pl) sum-=pr-pre[pr]; pr-=1; } while(pr<r) { pr+=1; if(pre[pr]>=pl) sum+=pr-pre[pr]; } return sum; }//莫队 void solve(int l,int r,int x,int y) { if( l>r ) return; int mid=l+r>>1,tmp,val=0x3f3f3f3f,pos; for(int i=x;i<mid&&i<=y;++i) { tmp=f[i][now-1]+cost(i+1,mid); if(tmp<val) val=tmp,pos=i; } f[mid][now]=val; solve(l,mid-1,x,pos); solve(mid+1,r,pos,y); } int main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); int n,k; cin>>n>>k; for(int i=1;i<=n;++i) { cin>>a[i]; pre[i]=last[a[i]]; last[a[i]]=i; } for(int i=1;i<=n;++i) { last[a[i]]=n+1; f[i][0]=0x3f3f3f3f; }//f[0][0]=0 for(int i=n;i;--i) { nex[i]=last[a[i]]; last[a[i]]=i; } for(int i=1;i<=k;++i) now=i,solve(1,n,0,n); cout<<f[n][k]<<'\n'; return 0; }