2019南昌網絡邀請賽-I-Max Answer-線段樹(RMQ)+ 單調棧

題目傳送門
在這裏插入圖片描述
思路:
先用單調棧算出每個a[i]可以作爲區間最小值的區間的最左邊和最右邊。
如果a[i] > 0的話,直接整個區間的值都要,因爲最小值爲正數的區間的數全>0;
如果a[i] < 0 的話,可以從點前位置,可以向左向右暴力搜索最小值。(數據有點水,所以可以過)。
更好的做法是用線段樹求維護每個點左邊和,右邊和的最大值。
但是線段樹是用來求解 解可以進行區間合併的,最常見的就是區間的max,min,和sum.那麼我們可以把這個問題轉化爲區間最值問題。假設要查尋的點爲a[i],他的左邊和右邊分別爲l,r.那麼對左邊,max(sum - pre[l-1] - suf[i])(sum爲所有數的總和,pre,suf,分別爲前綴和,後綴和。
這時,suf[i]是對於a[i]來說是固定的,所以建立樹時,可以不用考慮。最後求出結果後,記得-suf[i]就可以了。右邊同理。
也就是下面這個式子。

	LL lm = min(query_min(L[i]+1,i-1,1,0) - suf[i],1ll*0),rm = min(query_min(i+1,R[i]-1,1,1) - pre[i],1ll*0);

下面是線段樹版的代碼。

#include<bits/stdc++.h>
#define per(i,a,b) for(int i = (a);i <= (b);++i)
#define rep(i,a,b) for(int i = (a);i >= (b);--i)
#define INF 0x3f3f3f
using namespace std; 
typedef long long LL;
const int maxn = 5e5;
int n = 0,m = 0;
LL a[maxn+10];
LL pre[maxn+10],suf[maxn+10],sum = 0;
int L[maxn+10],R[maxn+10];
struct node{
	LL ma,mi;
	int l,r;
};
node tree[2][4*maxn+10];//0是往左邊的,1是往右邊的 
void build_tree(int l,int r,int root){
	tree[0][root].l = l,tree[0][root].r = r;
	tree[1][root].l = l,tree[1][root].r = r;
	
	if(l == r){
		tree[0][root].mi = tree[0][root].ma = sum - pre[l-1];
		tree[1][root].mi = tree[1][root].ma = sum - suf[l+1];
	}else{
		int mid = (l + r) >> 1;
		build_tree(l,mid,root<<1);
		build_tree(mid+1,r,root<<1|1);
		tree[0][root].mi = min(tree[0][root<<1].mi,tree[0][root<<1|1].mi);
		tree[0][root].ma = max(tree[0][root<<1].ma,tree[0][root<<1|1].ma);
		
		tree[1][root].mi = min(tree[1][root<<1].mi,tree[1][root<<1|1].mi);
		tree[1][root].ma = max(tree[1][root<<1].ma,tree[1][root<<1|1].ma);
	}
	return ;
}
LL query_max(int l,int r,int root,int id){
	if(tree[id][root].l > r || tree[id][root].r < l){
		return -1e18;
	}else if(l <= tree[id][root].l && r >= tree[id][root].r){
		return tree[id][root].ma;
	}else{
		return max(query_max(l,r,root<<1,id),query_max(l,r,root<<1|1,id));
	}
}
LL query_min(int l,int r,int root,int id){
	//if(l > r){
	//	return 0;
	//}
	if(tree[id][root].l > r || tree[id][root].r < l){
		return 1e18;
	}else if(l <= tree[id][root].l && r >= tree[id][root].r){
		return tree[id][root].mi;
	}else{
		return min(query_min(l,r,root<<1,id),query_min(l,r,root<<1|1,id));
	}
}

void Mo_stack(){
	stack<int> st;
	per(i,1,n){
		while(!st.empty() && a[i] <= a[st.top()]){
			st.pop();
		}
		if(st.empty()){
			L[i] = 0;
		}else{
			L[i] = st.top();
		}
		st.push(i);
	}
	
	while(!st.empty()){
		st.pop();
	}
	
	rep(i,n,1){
		while(!st.empty() && a[i] <= a[st.top()]){
			st.pop();
		}
		if(st.empty()){
			R[i] = n+1;
		}else{
			R[i] = st.top();
		}
		st.push(i);
	}
}
void init(){
	memset(pre,0,sizeof(pre)); memset(suf,0,sizeof(suf));
	sum = 0;	
	per(i,0,1){
		per(j,0,5e5*4){
			tree[i][j].mi = 1e18;	tree[i][j].ma = -1e18;
		}
	}	
}
int main(){
	while(~scanf("%d",&n)){
		init();
		per(i,1,n){
			scanf("%lld",&a[i]);
			pre[i] = pre[i-1] + a[i];
			sum += a[i];
		}
		rep(i,n,1){
			suf[i] = suf[i+1] + a[i];
		}
		Mo_stack();
		build_tree(1,n,1);
		LL maxv = -1e18;
		per(i,1,n){
			if(a[i] > 0){
				//LL lm = max(query_max(L[i]+1,i-1,1,0),1ll*0),rm = max(query_max(i+1,R[i]-1,1,1),1ll*0);
				//maxv = max(maxv,1ll*a[i]*(lm+a[i]+rm));
				maxv = max(maxv,1ll*a[i]*(pre[R[i]-1] - pre[L[i]]));//最小值爲正數的區間的數全>0,所以區間內的數全要 
			
			}else if(a[i] < 0){
				//左邊-suf[i],右邊-pre[i]。 
				LL lm = min(query_min(L[i]+1,i-1,1,0) - suf[i],1ll*0),rm = min(query_min(i+1,R[i]-1,1,1) - pre[i],1ll*0);
				maxv = max(maxv,1ll*a[i]*(lm+a[i]+rm));
			}else{
				maxv = max(maxv,1ll*0);
			}
		}
		printf("%lld\n",maxv);
	}
	
	return 0;
}
/*
4
-1 -1 -1 -1
4
-3 1 -2 1
*/

下面是暴力版本的代碼:

#include<bits/stdc++.h> 
using namespace std;
#define per(i,a,b) for(int i=(a);i<=(b);i++)
#define rep(i,a,b) for(int i=(a);i>=(b);i--)
#define pi acos(-1.0)
#define INF 0x3f3f3f3f
typedef long long LL;

const int maxn=5e5;
int n = 0,m = 0;
int a[maxn+10];
int L[maxn+10],R[maxn+10];
LL pre[maxn+10];
LL dp[maxn+10][20];
void Monotonous_stack(){
	stack<int> st;	
	per(i,1,n){
		while(!st.empty() && a[i] <= a[st.top()]){
			st.pop();
		}
		if(st.empty()){
			L[i] = 0;
		}else{
			L[i] = st.top();
		}
		st.push(i);
	}
	while(!st.empty()){
		st.pop();
	}
	
	rep(i,n,1){
		while(!st.empty() && a[i] <= a[st.top()]){
			st.pop();
		}
		if(st.empty()){
			R[i] = n+1;
		}else{
			R[i] = st.top();
		}
		st.push(i);
	}
}

void solve(){
	Monotonous_stack();

	LL maxv = -1e18;
	per(i,1,n){
		if(a[i] < 0){
			LL res = a[i];
			LL minv = 0,sum = 0;
			rep(j,i-1,L[i]+1){
				sum += a[j];
				minv = min(minv,sum);
			}
			res += minv;
			minv = 0,sum = 0;
			per(j,i+1,R[i]-1){
				sum += a[j];
				minv = min(minv,sum);
			}
			res += minv;
			maxv = max(maxv,res*a[i]);
		}else{
			maxv = max(maxv,(pre[R[i]-1] - pre[L[i]]) * a[i]);
		}
	}
	printf("%lld\n",maxv);
}
int main(){
	//std::ios::sync_with_stdio(false);
	//cin.tie(0) ;cout.tie(0);
	while(~scanf("%d",&n)){
		memset(pre,0,sizeof(pre));
		per(i,1,n){
			scanf("%d",&a[i]);
			pre[i] = a[i] + pre[i-1];
		} 
		solve();
	}
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章