CF940E Cashback(DP)
阿新 • • 發佈:2020-09-09
題意:
給定一個數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]; }