CF868F Yet Another Minimization Problem
题目描述
Solution
一开始可以很容易地写出一个
d
p
dp
dp式子:
设
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i个数分成
j
j
j段的最小代价,有:
f
i
,
j
=
min
k
=
1
i
−
1
f
k
,
j
−
1
+
C
k
+
1
,
i
f_{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
C
x
,
y
C_{x,y}
Cx,y表示求见
a
[
x
.
.
y
]
a[x..y]
a[x..y]中有多少个相同的数对。
我们可以发现这样一个性质:
设有整数
x
<
y
≤
i
x<y\leq i
x<y≤i。
显然
Δ
C
x
,
i
>
Δ
C
y
,
i
\Delta C_{x,i}>\Delta C_{y,i}
ΔCx,i>ΔCy,i,也就是说在前面的
C
C
C增长比较快。
考虑决策单调性:
若存在整数
x
<
y
≤
i
x<y\leq i
x<y≤i,使得
f
x
,
j
+
C
x
+
1
,
i
>
f
y
,
j
+
C
y
+
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
则对于所有的
i
′
>
i
i'>i
i′>i,显然有
f
x
,
j
+
C
x
+
1
,
i
′
>
f
y
,
j
+
C
y
+
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′
也就是说,如果前面的比后面的劣,那么它在之后的转移中都不会作为最优决策点出现了。
这里就可以用决策单调性的一个套路做法——分治。
若我们要求的答案为
f
x
.
.
y
,
j
f_{x..y,j}
fx..y,j,当前的决策点区间为
[
X
,
Y
]
[X,Y]
[X,Y]。
我们可以先对于
m
i
d
=
⌊
x
+
y
2
⌋
mid=\lfloor \frac{x+y}{2} \rfloor
mid=⌊2x+y⌋求出
f
m
i
d
,
j
f_{mid,j}
fmid,j以及它的最优决策点
k
k
k,这一步可以暴力枚举
X
.
.
Y
X..Y
X..Y求得,时间复杂度
O
(
Y
−
X
)
O(Y-X)
O(Y−X)。
然后显然 f x . . m i d − 1 , j f_{x..mid-1,j} fx..mid−1,j的最优决策点 k ′ ≤ k k'\leq k k′≤k, f m i d + 1.. y , j f_{mid+1..y,j} fmid+1..y,j的最优决策点 k ′ ≥ k k'\geq k k′≥k,分治计算即可。
这里转移中要用到的 C x , y C_{x,y} Cx,y可以类似莫队的方法,每次或减去加入一个数,计算贡献的方式暴力求。
显然分治一遍的时间复杂度为 T ( n ) = T ( n / 2 ) + O ( n ) = O ( n l g n ) T(n)=T(n/2)+O(n)=O(nlgn) T(n)=T(n/2)+O(n)=O(nlgn),一共要做 m m m遍,所以总时间复杂度为 O ( m n l g n ) 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 second
using 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;
}