一 线段树
一个子段肯定是由两个前缀做差相得 那么对于每一个前缀 就看它前边比它小的最大的前缀 做差取最大值 线段树维护一下
#include <bits/stdc++.h> using namespace std; #define ll long long #define N 0x3f3f3f3f3f3f3f3f struct node { int l; int r; ll val; }; map <ll,int> mp; node tree[200010]; ll sum[50010],pre[50010]; int n,m; void pushup(int cur) { tree[cur].val=max(tree[2*cur].val,tree[2*cur+1].val); return; } void build(int l,int r,int cur) { int m; tree[cur].l=l; tree[cur].r=r; tree[cur].val=-N; if(l==r) return; m=(l+r)/2; build(l,m,2*cur); build(m+1,r,2*cur+1); return; } ll query(int pl,int pr,int cur) { ll res; if(pl<=tree[cur].l&&tree[cur].r<=pr) { return tree[cur].val; } res=-N; if(pl<=tree[2*cur].r) res=max(res,query(pl,pr,2*cur)); if(pr>=tree[2*cur+1].l) res=max(res,query(pl,pr,2*cur+1)); return res; } void update(int tar,int cur) { if(tree[cur].l==tree[cur].r) { tree[cur].val=pre[tree[cur].l]; return; } if(tar<=tree[2*cur].r) update(tar,2*cur); else update(tar,2*cur+1); pushup(cur); return; } int main() { ll val,ans; int i,l,r; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lld",&val); sum[i]=sum[i-1]+val; pre[i]=sum[i]; } mp.clear(); sort(pre+1,pre+n+1); for(i=1,m=0;i<=n;i++) { if(m==0||pre[m]!=pre[i]) { pre[++m]=pre[i]; mp[pre[m]]=m; } } build(1,m,1); ans=N; for(i=1;i<=n;i++) { if(sum[i]>0) ans=min(ans,sum[i]); } for(i=1;i<=n;i++) { l=1,r=mp[sum[i]]-1; if(l<=r) { val=query(1,mp[sum[i]]-1,1); if(val!=-N) { ans=min(ans,sum[i]-val); } } update(mp[sum[i]],1); } printf("%lld\n",ans); return 0; }
二 贪心排序
上一个解法是按端点来考虑 这个解法是看别人的 按照取值来考虑 一个正子段肯定是一个在后的大前缀减去在前的小前缀 所以先按前缀的值升序排序 又因为同值的前缀作差为零没意义 所以同值前缀位置越靠后则排序是越往前放
这样所有的前缀首先按取值分成了很多段 每段取值相同 而两端交界处因为“同值前缀位置越靠后则排序是越往前放”所以会在这里产生最优解
#include <bits/stdc++.h> using namespace std; #define ll long long #define N 0x3f3f3f3f3f3f3f3f struct node { ll sum; int pos; }; node pre[50010]; int n; bool cmp(node n1,node n2) { if(n1.sum==n2.sum) return n1.pos>n2.pos; else return n1.sum<n2.sum; } int main() { ll val,minn; int n,i; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lld",&val); pre[i].sum=pre[i-1].sum+val; pre[i].pos=i; } sort(pre+1,pre+n+1,cmp); minn=N; for(i=1;i<=n;i++) { if(pre[i].sum>0) { minn=min(minn,pre[i].sum); } } for(i=1;i<=n;i++) { if(pre[i].pos>pre[i-1].pos&&pre[i].sum>pre[i-1].sum) { minn=min(minn,pre[i].sum-pre[i-1].sum); } } printf("%lld\n",minn); return 0; }