You are given an array of n integers a1... an. The cost of a subsegment is the number of unordered pairs of distinct indices within the subsegment that contain equal elements. Split the given array into k non-intersecting non-empty subsegments so that the sum of their costs is minimum possible. Each element should be present in exactly one subsegment.
The first line contains two integers n and k (2 ≤ n ≤ 105, 2 ≤ k ≤ min (n, 20)) — the length of the array and the number of segments you need to split the array into.
The next line contains n integers a1, a2, ..., an (1 ≤ ai ≤ n) — the elements of the array.
Print single integer: the minimum possible total cost of resulting subsegments.
7 3 1 1 3 3 3 2 1
1
10 2 1 2 1 2 1 2 1 2 1 2
8
13 3 1 2 2 2 1 2 1 1 1 2 2 1 1
9
DP的决策单调性:
http://blog.csdn.net/jaihk662/article/details/78174717
先看这个DP方程:dp[x] = min(dp[y]+w[y, x])
(其中w[x, y]就是区间[x, y]中相同数字对个数,而题目就是让你求k次dp[1]到dp[n])
和1D/1D动态规划又是那么的像,经过一通证(xia)明(cai),可以得出该DP也具有决策单调性
也就是假设j点是当前i点的最优决策,那么对于所有的x>i,最优决策p(x)一定都大于j,对于所有的x<i,最优决策p(x)一定都小于j
可是这题不能像上面链接中那样利用栈来解决,因为你无法O(1)得出j点转移更优还是i点转移更优
也就是你无法O(1)算出w[y, x]!
……
但是你可以O(1)转移啊,也就是只要知道w[y, x],那么你就可以O(1)求出
w[y+1, x]、w[y-1, x]、w[y, x+1]、w[y, x-1]
所以考虑二分+莫队
假设n=30,具体步骤如下:
先求出dp[15],利用莫队转移检测所有的w[x, 15],这个时候可以得出最优转移点y满足dp[15] = dp[y]+w[y, 15]
之后二分左半区间求出dp[7],但这时就不用暴力检测所有的w[x', 7]了,因为你的转移点x'一定满足x'<y
同时还要二分右半区间dp[23],同上!这个时候只用暴力检测所有的w[y, 23]即可
……
这样重复k次,最后的dp[n]就是答案
复杂度分析:
二分复杂度O(nlogn)
每次莫队时,L都一定会遍历整个二分区间(的一半),所以L对复杂度贡献为O(nlogn),而每次二分都可以确定一个当前最优转移点,当二分左侧时,R一定在最优转移点左侧移动,当二分右侧时,R一定在最优转移点右侧移动,这就确保了R对复杂度贡献也为O(nlogn)
总共进行k次dp,所以总复杂度为O(knlogn)
函数传递的l, r是当前二分区间,传递的L, R是决策区间,也就是最优决策点一定在这个范围内
#include<stdio.h>
#include<string.h>
#define LL long long
LL now, a[100005], sum[100005], dp[22][100005];
void Sech(LL l, LL r, LL L, LL R, LL val)
{
LL m, x, i;
if(l>r)
return;
m = (l+r)/2;
for(i=l;i<=m;i++)
val += sum[a[i]], sum[a[i]]++;
x = 0;
for(i=L;i<=R&&i<=m;i++)
{
sum[a[i]]--;
val -= sum[a[i]];
if(val+dp[now-1][i]<dp[now][m])
{
x = i;
dp[now][m] = val+dp[now-1][i];
}
}
for(i=L;i<=R&&i<=m;i++)
val += sum[a[i]], sum[a[i]]++;
for(i=l;i<=m;i++)
sum[a[i]]--, val -= sum[a[i]];
Sech(l, m-1, L, x, val);
for(i=l;i<=m;i++)
val += sum[a[i]], sum[a[i]]++;
for(i=L;i<x;i++)
sum[a[i]]--, val -= sum[a[i]];
Sech(m+1, r, x, R, val);
for(i=L;i<x;i++)
sum[a[i]]++;
for(i=l;i<=m;i++)
sum[a[i]]--;
}
int main(void)
{
LL n, m, i;
scanf("%I64d%I64d", &n, &m);
for(i=1;i<=n;i++)
scanf("%I64d", &a[i]);
memset(dp, 62, sizeof(dp));
dp[1][0] = 0;
for(i=1;i<=n;i++)
{
dp[1][i] = dp[1][i-1]+sum[a[i]];
sum[a[i]]++;
}
for(i=2;i<=m;i++)
{
now = i;
memset(sum, 0, sizeof(sum));
Sech(i-1, n, i-1, n, 0);
}
printf("%I64d\n", dp[m][n]);
return 0;
}
/*
7 3
1 1 3 3 3 2 1
*/