题意:
给出一个长度为n的数组a,现在需要将该数组分成任意段,每段的贡献为:
问你贡献最大和。
题解:
首先分析一下:
设一段区间和为sum;
当sum<0时,我们把他们拆成一段段区间为1,贡献只大不小;
sum==0时,我们从中间拆开,假如两段都小于零,那么总贡献不变;假如有大于0的,那么总值变大;再把小于0的子区间做如下拆分,最后都会变成大于0的区间和小于0且长度为1的区间。
那么只剩下sum>0的区间了。显然满足dp性质,无后效性,子问题性。那么我们设为前i个数组成的区间的最大贡献。
,即:。
那么我们每次dp只要找到的最大值即可求解。
那么我们需要在小于的复杂度内求得的最大值。
由于不具有单调性,考虑树状数组优化,在前缀和小于当前前缀和的j中找的最大值即可求解。(保证区间和大于0)
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int inf=0x3f3f3f3f;
const int N=5e5+5;
int n,cnt,dp[N],c[N];
ll a[N],pre[N];
set<ll> st;
map<ll,int> id;
void add(int x,int k){
while(x<=n)
c[x]=max(c[x],k),x+=(x&-x);
}
int query(int x){
int cnt=-inf;
while(x>0)
cnt=max(cnt,c[x]),x-=(x&-x);
return cnt;
}
void solve(){
cin>>n;cnt=0; //初始化
st.clear(); id.clear();
for(int i=1;i<=n;i++)
dp[i]=-inf,c[i]=-inf;
st.insert(0);
for(int i=1;i<=n;i++)
{
cin>>a[i];
pre[i]=pre[i-1]+a[i];
st.insert(pre[i]);
}
for(ll p:st) //对前缀和进行离散化
id[p]=++cnt;
add(id[0],0); //将起始无元素状态时的0状态加入树状数组中
for(int i=1;i<=n;i++)
{
if(a[i]<0)
dp[i]=dp[i-1]-1;
else if(a[i]==0)
dp[i]=dp[i-1];
dp[i]=max(dp[i],query(id[pre[i]]-1)+i);
add(id[pre[i]],dp[i]-i);
}
cout<<dp[n]<<endl;
}
int main(){
ios::sync_with_stdio(false);
solve();
}