CF #439 E Another Maximum Problem
分治·决策单调性
题解:
暴力Dp:
设 f[i][j] 表示前i个分成j段的最小花费。
f[i][j]=f[k][j−1]+cost(k+1,i)
cost=∑icnt[i](cnt[i]−1)2
时间复杂度: O(n2k)
优化:
决策具有单调性。
(打表?)
那这个单调性怎么用呢?
以前做过一个单调队列维护决策区间的。
但是那样要求能快速计算出cost,而这里的cost显然要维护。
那就分治。
[L,R] 表示当前分治的区域,它由 [l,r] 转移过来。
先解决一下 [L,R] 的mid从那里转以来,记为p,
然后向下分治:solve(l,p,L,mid-1),solve(p,r,mid+1,r)
那么如何计算cost呢?
像莫队那样维护。
一个lpos指针一个rpos指针表示当前统计的区间,根据需要移动两个指针。
调用solve前保证lpos=l-1,rpos=L-1。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n,a[N],cnt[N]; ll f[21][N];
void solve(int l,int r,int L,int R,int K,ll cur){
if(L>R) return;
// D(l); D(r); D(L); D(R); D(K); D(cur); E;
int mid=(L+R)>>1, p;
for(int i=L;i<=mid;i++) cur+=cnt[a[i]]++;
for(int i=l;i<=mid && i<=r;i++){
cur-=--cnt[a[i]];
if(f[K][mid]>f[K-1][i]+cur){
f[K][mid]=f[K-1][i]+cur; p=i;
}
}
// D(mid); D(p); D(f[K][mid]); E;
for(int i=L;i<=mid;i++) cur-=--cnt[a[i]];
for(int i=l;i<=mid && i<=r;i++) cur+=cnt[a[i]]++;
solve(l,p,L,mid-1,K,cur);
for(int i=L;i<=mid;i++) cur+=cnt[a[i]]++;
for(int i=l;i<p;i++) cur-=--cnt[a[i]];
solve(p,r,mid+1,R,K,cur);
for(int i=L;i<=mid;i++) cur-=--cnt[a[i]];
for(int i=l;i<p;i++) cur+=cnt[a[i]]++;
}
int main(){
freopen("6.in","r",stdin);
int n,K; ll cur=0;
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++) scanf("%d",a+i);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++){
cur+=cnt[a[i]]++;
f[1][i]=cur;
}
for(int i=2;i<=K;i++){
memset(cnt,0,sizeof(cnt));
solve(1,n,1,n,i,0);
}
printf("%lld\n",f[K][n]);
}