题目:
题解:
首先我们可以列出一个60pts的DP式
表示i和i-1有没有相连,前i个分成j组的最小总长
那么转移很简单
但是这样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);
}