题目:
题解:
首先我们可以列出一个60pts的DP式
f[0/1][i][j]
f
[
0
/
1
]
[
i
]
[
j
]
表示i和i-1有没有相连,前i个分成j组的最小总长
那么转移很简单
f[0][i][j]=min(f[0][i−1][j],f[1][i−1][j])
f
[
0
]
[
i
]
[
j
]
=
m
i
n
(
f
[
0
]
[
i
−
1
]
[
j
]
,
f
[
1
]
[
i
−
1
]
[
j
]
)
f[1][i][j]=f[0][i−1][j−1]+s[i]−s[i−1]
f
[
1
]
[
i
]
[
j
]
=
f
[
0
]
[
i
−
1
]
[
j
−
1
]
+
s
[
i
]
−
s
[
i
−
1
]
但是这样n^2肯定过不去
我们觉得很像省选一轮的D2T2
现在发现随着j的增加,这个函数的值一定是增的(废话),并且是下凸的,这也不难理解,因为我们一开始肯定是先选增值小的,然后选增加大的
考虑二分把枚举j的一维省略,我们按照套路,使每分一组获得一个代价来控制选的组数量
特别注意有切不到的情况,那么在<=k的时候我们都要更新答案
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const int N=100005;
int s[N],n;LL mid;
struct hh{LL x,y;}f[2][N];
hh operator +(hh a,int b){return (hh){a.x+b,a.y};}
bool operator <(hh a,hh b){return a.x<b.x;}
hh add(hh a){return (hh){a.x-mid,a.y+1};}
void work()
{
memset(f,0x7f,sizeof(f));
f[0][1].x=0; f[0][1].y=0;
for (int i=2;i<=n;i++)
{
f[0][i]=min(f[0][i-1],f[1][i-1]);
f[1][i]=add(f[0][i-1]+(s[i]-s[i-1]));
}
f[0][n]=min(f[1][n],f[0][n]);
}
int main()
{
int K;scanf("%d%d",&n,&K);
LL l=0,r=0,ans=0;
for (int i=1;i<=n;i++) scanf("%d",&s[i]),r+=(LL)s[i];
while (l<=r)
{
mid=(l+r)>>1;
work();
if (f[0][n].y<=K) ans=f[0][n].x+(LL)K*mid,l=mid+1;else r=mid-1;//r=mid-1;else l=mid+1;
}
printf("%lld",ans);
}