CF868F Yet Another Minimization Problem
题目描述
Solution
一开始可以很容易地写出一个dpdpdp式子:
设fi,jf_{i,j}fi,j表示前iii个数分成jjj段的最小代价,有:
fi,j=mink=1i−1fk,j−1+Ck+1,if_{i,j}=\min_{k=1}^{i-1}f_{k,j-1}+C_{k+1,i} fi,j=k=1mini−1fk,j−1+Ck+1,i
Cx,yC_{x,y}Cx,y表示求见a[x..y]a[x..y]a[x..y]中有多少个相同的数对。
我们可以发现这样一个性质:
设有整数x<y≤ix<y\leq ix<y≤i。
显然ΔCx,i>ΔCy,i\Delta C_{x,i}>\Delta C_{y,i}ΔCx,i>ΔCy,i,也就是说在前面的CCC增长比较快。
考虑决策单调性:
若存在整数x<y≤ix<y\leq ix<y≤i,使得
fx,j+Cx+1,i>fy,j+Cy+1,if_{x,j}+C_{x+1,i}>f_{y,j}+C_{y+1,i} fx,j+Cx+1,i>fy,j+Cy+1,i
则对于所有的i′>ii'>ii′>i,显然有
fx,j+Cx+1,i′>fy,j+Cy+1,i′f_{x,j}+C_{x+1,i'}>f_{y,j}+C_{y+1,i'} fx,j+Cx+1,i′>fy,j+Cy+1,i′
也就是说,如果前面的比后面的劣,那么它在之后的转移中都不会作为最优决策点出现了。
这里就可以用决策单调性的一个套路做法——分治。
若我们要求的答案为fx..y,jf_{x..y,j}fx..y,j,当前的决策点区间为[X,Y][X,Y][X,Y]。
我们可以先对于mid=⌊x+y2⌋mid=\lfloor \frac{x+y}{2} \rfloormid=⌊2x+y⌋求出fmid,jf_{mid,j}fmid,j以及它的最优决策点kkk,这一步可以暴力枚举X..YX..YX..Y求得,时间复杂度O(Y−X)O(Y-X)O(Y−X)。
然后显然fx..mid−1,jf_{x..mid-1,j}fx..mid−1,j的最优决策点k′≤kk'\leq kk′≤k,fmid+1..y,jf_{mid+1..y,j}fmid+1..y,j的最优决策点k′≥kk'\geq kk′≥k,分治计算即可。
这里转移中要用到的Cx,yC_{x,y}Cx,y可以类似莫队的方法,每次或减去加入一个数,计算贡献的方式暴力求。
显然分治一遍的时间复杂度为T(n)=T(n/2)+O(n)=O(nlgn)T(n)=T(n/2)+O(n)=O(nlgn)T(n)=T(n/2)+O(n)=O(nlgn),一共要做mmm遍,所以总时间复杂度为O(mnlgn)O(mnlgn)O(mnlgn)。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
ll C=0,f[21][MAXN];
int n,m,L=1,R=0,a[MAXN],cnt[MAXN];
ll getans(int l,int r)
{while (L>l) C+=cnt[a[--L]]++;while (L<l) C-=--cnt[a[L++]];while (R<r) C+=cnt[a[++R]]++;while (R>r) C-=--cnt[a[R--]];return C;
}
void solve(int now,int x,int y,int X,int Y)
{int mid=(x+y)>>1,t;f[now][mid]=loo;for (int i=X;i<=Y;i++) t=upmin(f[now][mid],f[now-1][i]+getans(i+1,mid))?i:t;if (x==y) return;solve(now,x,mid,X,t);solve(now,mid+1,y,t,Y);
}
int main()
{n=read(),m=read(),L=1,R=C=0;for (int i=1;i<=n;i++) a[i]=read(),f[1][i]=getans(1,i);for (int i=2;i<=m;i++) solve(i,1,n,1,n);printf("%lld\n",f[m][n]);return 0;
}