思路:
dp+rmq
可以证明最后划分的区间可以由长度为1和长度为c的区间组成的,这样就可以用O(n)的dp求了,区间最小值随便拿什么维护都可以
状态:dp[i]表示到i这个位置为止的最小划分和
初始状态:dp[0]=0
目标状态:dp[n]
状态转移:dp[i]=min(dp[i-1]+a[i],dp[i-c]+sum[i]-sum[i-c]+minmum(i-c+1,i))
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mp make_pair #define ls rt<<1,l,m #define rs rt<<1|1,m+1,r #define pli pair<ll,int> #define mem(a,b) memset(a,b,sizeof(a)) const int N=1e5+5; const int INF=0x7f7f7f7f; int a[N],tree[N<<2],cnt; ll sum[N],dp[N]; void push_up(int rt){ tree[rt]=min(tree[rt<<1],tree[rt<<1|1]); } void build(int rt,int l,int r){ if(l==r){ cin>>a[++cnt]; tree[rt]=a[cnt]; return ; } int m=(l+r)>>1; build(ls); build(rs); push_up(rt); } int query(int L,int R,int rt,int l,int r){ if(L<=l&&r<=R){ return tree[rt]; } int ans=INF,m=(l+r)>>1; if(L<=m)ans=min(ans,query(L,R,ls)); if(R>m)ans=min(ans,query(L,R,rs)); return ans; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n,c; cin>>n>>c; build(1,1,n); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++){ if(i-c>=0)dp[i]=min(dp[i-1]+a[i],dp[i-c]+sum[i]-sum[i-c]-query(i-c+1,i,1,1,n)); else dp[i]=dp[i-1]+a[i]; } cout<<dp[n]<<endl; return 0; }