ICPC China Nanchang National -- Max answer(ST表+前后缀+线段树+二分)

题目链接:https://nanti.jisuanke.com/t/38228

题目大意:给出n个元素的数组,找出一个区间[l,r]使得存在一个最大的答案ans满足:

ans=max((\sum_{i=l}^{r}a[i])*min(a[i])(i\in [l,r])),输出ans即可。

思路:有点像CF里面的一道题的升级版:https://codeforces.com/contest/1108/problem/E2

这是这套题的题解:https://blog.csdn.net/qq_40482358/article/details/86668554

我们可以设每个a[i]为区间[li,ri]的最小值,然后遍历a[i]查找ans即可。

如何得到ans?我们首先对数组a预处理出它的ST表,然后我们就可以根据区间的最小值来二分它的范围,得出区间[l,i]和[i,r]。

用线段树来维护一下数组a的前缀合和后缀和的区间最值。

得到区间[l,i]和区间[i,r]之后,我们就可以求出 max(SufFix[l,i])+max(PreFix[i,r])*a[i] 这是以a[i]为最小值时的最大的ans。

因为求左区间[l,i]中一点k,使得存在最大||最小的SufFix[k]-SufFix[i]最大,由于SufFix[i]不变,所以用后缀和。

同理前缀和也是PreFix[i]不变。

当然我们可以也可以都写成前缀和的形式。右区间不变,即存在最大||最小的 PreFix[i]-PreFix[k],由于PreFix[i]不变,因此我们找最大的-PreFix[k]即可。

对于每次找到的ans,刷新即可。

ACCode:

// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")

#include<stdio.h>
#include<string.h>
#include<math.h>

#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;

#define ll long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=5e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
const double EPS=1.0e-8;
const double PI=acos(-1.0);

class ST{
	ll STmax[MAXN][20],STmin[MAXN][20],mn[MAXN];
	
	public : void IntST(int n,ll a[]){
		mn[0]=-1;
		for(int i=1;i<=n;++i){
			mn[i]=((i&(i-1))==0)?mn[i-1]+1:mn[i-1];
			STmax[i][0]=STmin[i][0]=a[i];
		}
		for(int j=1;j<=mn[n];++j){
			for(int i=1;i+(1<<j)-1<=n;++i){
				STmax[i][j]=max(STmax[i][j-1],STmax[i+(1<<(j-1))][j-1]);
				STmin[i][j]=min(STmin[i][j-1],STmin[i+(1<<(j-1))][j-1]);
			}
		}
	}
	public : ll RMQMAX(int l,int r){
		int k=mn[r-l+1];
		return max(STmax[l][k],STmax[r-(1<<k)+1][k]);
	}
	public : ll RMQMIN(int l,int r){
		int k=mn[r-l+1];
		return min(STmin[l][k],STmin[r-(1<<k)+1][k]);
	}
};
class Segment{
	ll TreeMax[MAXN<<2],TreeMin[MAXN<<2];
	
	public : void Build(int l,int r,int rt,ll a[]){
		if(l==r){
			TreeMax[rt]=a[l];TreeMin[rt]=a[l];
			return ;
		}int mid=(l+r)>>1;
		Build(l,mid,rt<<1,a);Build(mid+1,r,rt<<1|1,a);
		TreeMax[rt]=max(TreeMax[rt<<1],TreeMax[rt<<1|1]);
		TreeMin[rt]=min(TreeMin[rt<<1],TreeMin[rt<<1|1]);
	}
	public : ll QueryMax(int ql,int qr,int l,int r,int rt){
		if(ql<=l&&r<=qr){
			return TreeMax[rt];
		}int mid=(l+r)>>1;
		ll res=-INF64;
		if(ql<=mid) res=max(res,QueryMax(ql,qr,l,mid,rt<<1));
		if(qr>mid) res=max(res,QueryMax(ql,qr,mid+1,r,rt<<1|1));
		return res;
	}
	public : ll QueryMin(int ql,int qr,int l,int r,int rt){
		if(ql<=l&&r<=qr){
			return TreeMin[rt];
		}int mid=(l+r)>>1;
		ll res=INF64;
		if(ql<=mid) res=min(res,QueryMin(ql,qr,l,mid,rt<<1));
		if(qr>mid) res=min(res,QueryMin(ql,qr,mid+1,r,rt<<1|1));
		return res;
	}
	public : void Show(int l,int r,int rt){
		cout<<l<<" "<<r<<" : "<<TreeMax[rt]<<" - "<<TreeMin[rt]<<endl;
		if(l==r) return ;
		int mid=(l+r)>>1;
		Show(l,mid,rt<<1);Show(mid+1,r,rt<<1|1);
	}
};
ST St;
Segment SegPre,SegSuf;
ll a[MAXN];
ll PreFix[MAXN],SufFix[MAXN];
int n;

int GetL(ll Key,int l,int r){
	int x=r;
	while(l<=r){
		int mid=(l+r)>>1;
		if(St.RMQMIN(mid,x)<Key) l=mid+1;
		else r=mid-1;
	}return l;
}
int GetR(ll Key,int l,int r){
	int x=l;
	while(l<=r){
		int mid=(l+r)>>1;
		if(St.RMQMIN(x,mid)>=Key) l=mid+1;
		else r=mid-1;
	}return r;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for(int i=1;i<=n;++i) PreFix[i]=PreFix[i-1]+a[i];
//	cout<<"PreFix: ";for(int i=1;i<=n;++i) cout<<PreFix[i]<<" ";cout<<endl;
	for(int i=n;i>=1;--i) SufFix[i]=SufFix[i+1]+a[i];
//	cout<<"SufFix: ";for(int i=1;i<=n;++i) cout<<SufFix[i]<<" ";cout<<endl;
	St.IntST(n,a);//Query: [i,j] k=int(log((j-i+1)*1.0)/log(2.0));  max(ST[i][k],ST[j-(1<<k)+1][k]);
//	for(int i=1;i<=n;++i){
//		for(int j=i;j<=n;++j){
//			printf("l~r:[%d,%d]:max=%lld min=%lld\n",i,j,St.RMQMAX(i,j),St.RMQMIN(i,j));
//		}
//	}printf("\n");
	SegPre.Build(1,n,1,PreFix);SegSuf.Build(1,n,1,SufFix);
//	SegPre.Show(1,n,1);cout<<endl;
//	SegSuf.Show(1,n,1);
//	cout<<"min[1,1]:"<<SegPre.QueryMin(1,1,1,n,1)<<endl;
	ll ans=0;
	for(int i=1;i<=n;++i){
		int l=GetL(a[i],1,i),r=GetR(a[i],i,n);
		ll resL,resR;
//		cout<<"区间l,i,r:"<<l<<" "<<i<<" "<<r<<endl;
		if(a[i]>0){
			resL=SegSuf.QueryMax(l,i,1,n,1);
			resR=SegPre.QueryMax(i,r,1,n,1);
		}
		else{
			resL=SegSuf.QueryMin(l,i,1,n,1);
			resR=SegPre.QueryMin(i,r,1,n,1);
		}
		resL=resL-SufFix[i];resR=resR-PreFix[i];
//		cout<<"resL,resR: "<<resL<<" "<<resR<<endl;
		ans=max(ans,1ll*(resL+resR+a[i])*a[i]);
//		cout<<ans<<endl;
	}printf("%lld\n",ans);
}
/*

5
1 2 3 4 5
36

*/

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/89422999
今日推荐