题意:

给定一个数C,对一个序列作切割,每一段对答案的贡献是这一段的元素之和减去这一段里最小的Len/C个数之和,Len指这一段的长度。

询问最小答案。

题解:

有一个结论是,两个长度为C的序列对答案的贡献一定小于这两个序列合并起来对答案的贡献。

有个贪心的做法就是只切长度为1和长度为C的序列。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
ll dp[maxn];
ll c[maxn];
ll a[maxn];
ll n,m;
struct node {
    ll l,r,sum;
}segTree[maxn<<2];
void build (ll i,ll l,ll r) {
    segTree[i].l=l;
    segTree[i].r=r;
    if (l==r) {
        segTree[i].sum=a[l];
        return;
    }
    ll mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    segTree[i].sum=min(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
ll query (int i,int l,int r) {
    if (segTree[i].l>=l&&segTree[i].r<=r) 
        return segTree[i].sum;
    ll mid=(segTree[i].l+segTree[i].r)>>1;
    ll ans=1e9;
    if (l<=mid)
        ans=min(ans,query(i<<1,l,r));
    if (r>mid)
        ans=min(ans,query(i<<1|1,l,r));
    return ans;
}
int main () {
    cin>>n>>m;
    for (int i=1;i<=n;i++) dp[i]=1e18;
    for (int i=1;i<=n;i++) cin>>a[i];
    for (int i=1;i<=n;i++) c[i]=c[i-1]+a[i];
    build(1,1,n);
    for (int i=1;i<=n;i++) {
        dp[i]=min(dp[i],dp[i-1]+a[i]);
        if (i>=m)
            dp[i]=min(dp[i],dp[i-m]+c[i]-c[i-m]-query(1,i-m+1,i));
    }
    cout<<dp[n];
}