题意:
给定一个数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]; }