题目大意:给你一个数组,让你选中这个数组中的一个子段,求出子段中最大的三个值b1,b2,b3,让这个公式b1+b2+b3-(r-l)的值最大。这里给出两个思路。
解法一:
分解公式,b1+b2+b3-(r-l)可以分解成:b1+l+b2+b3-r;这里我们预处理左边b1+l的最大值,每添加一个元素便有一个最大值,这样我们可以处理出从左到右每个1->l的最大值,用ml数组来存储这个值;然后以同样的方法来预处理n->r的b3-r最大值,用mr来存储。求得每个区间的最大值可以用大根堆实现(就是优先队列)。最后我们枚举b2,加上ml[i-1]的最大值和mr[i+1]的最大值,最后取最大。
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn=1e6+7; int ml[maxn],mr[maxn],a[maxn]; int n,t; void solve(){ cin>>n; priority_queue<int> q,p; for(int i=1;i<=n;i++){ cin>>a[i]; q.push(a[i]+i); ml[i]=q.top(); } for(int i=n;i>=1;i--){ p.push(a[i]-i); mr[i]=p.top(); } int Max=0; for(int i=2;i<n;i++){ Max=max(Max,a[i]+ml[i-1]+mr[i+1]); } cout<<Max<<"\n"; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>t; while(t--){ solve(); } return 0; }
解法二:
线段树法(虽然可能用不了这么麻烦)。
首先我们同样是分解公式:b1+b2+b3-(r-l)变成,b1+b2+b3-(r-mid)-(mid-l),即b1-(mid-l)+b2+b3-(r-mid)。由此公式我们可以看出我们只需要枚举b2,用一个数据结构来维护b1-(mid-l)和b3-(r-mid)——线段树。注意在初始化时一定要把a数组减去当前元素最开始枚举的地点的距离(我这里是2,因为我要求i-1和i+1的最大值)。每次向右移动时即(i++),左边区间要-1(距离增大),右边区间+1(因为距离减小,注意i这个位置也要修改)。
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn=1e6+7; struct SG_Tree{ int l,r; int max; int add; }st[maxn]; int t,n; int a[maxn]; void updata(int pos){ st[pos].max=max(st[pos<<1].max,st[pos<<1|1].max); } void push(int pos){ st[pos<<1].max+=st[pos].add; st[pos<<1|1].max+=st[pos].add; st[pos<<1].add+=st[pos].add; st[pos<<1|1].add+=st[pos].add; st[pos].add=0; } void build(int pos,int l,int r){ st[pos].l=l; st[pos].r=r; st[pos].add=0; st[pos].max=0; if(l==r){ st[pos].max=a[l]; return ;//日常忘记返回而sg } int mid=(l+r)>>1; build(pos<<1,l,mid); build(pos<<1|1,mid+1,r); updata(pos); } void changeadd(int pos,int s,int t,int k){ if(s<=st[pos].l&&t>=st[pos].r){ st[pos].add+=k; st[pos].max+=k;//max的修改可以直接加减 return ; } int mid=(st[pos].l+st[pos].r)>>1; push(pos); if(s<=mid) changeadd(pos<<1,s,t,k); if(t>mid) changeadd(pos<<1|1,s,t,k); updata(pos); } int getmax(int pos,int s,int t){ if(s<=st[pos].l&&st[pos].r<=t){ return st[pos].max; } int mid=(st[pos].l+st[pos].r)>>1; push(pos); int Max=0; if(s<=mid) Max=max(getmax(pos<<1,s,t),Max); if(t>mid) Max=max(getmax(pos<<1|1,s,t),Max); return Max; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>t; while(t--){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; if(i<=2) a[i]-=(2-i); else a[i]-=(i-2); }//注意这里我们要从mid=2开始枚举b2,左右两边的值都要减去和mid之间的距离。 build(1,1,n); int sum=0; for(int i=2;i<n;i++){ if(i!=2){//i==2的时候已经处理过了 changeadd(1,1,i-1,-1); changeadd(1,i,n,1);//注意这里从i开始 //如:-1 0 -1 -2 //之后变成:-2 -1 0 -1 a[i]+=(i-2);//注意a数组要跟线段树的修改的值要同步,我卡了半天。。 } sum=max(sum,a[i]+getmax(1,1,i-1)+getmax(1,i+1,n)); } cout<<sum<<"\n"; } return 0; }