ICPC China Nanchang National Invitational - I. Max answer(线段树+ST)

题目链接
N个数字求一个区间使得 i = l i = r a [ i ] × i = l i = r m i n ( a [ i ] ) \sum_{i=l}^{i=r} a[i] × \sum_{i=l}^{i=r}min(a[i]) 最大

思路

  • 枚举区间的最小值为 a [ i ] a[i] ,根据ST表二分找到它 最左 L L 最右 R R 端点,这是保证区间 [ L , R ] [L, R] 的最小值为 a [ i ] a[i]
    p r e pre 记录前缀和
    s u f suf 记录后缀和

找到区间之后分两种情况

  • a [ i ] 0 a[i] \ge0
    A n s = a [ i ] × ( M a x P r e [ i , R ] P r e [ i 1 ] + M a x S u f [ L , i ] S u f [ i ] ) Ans = a[i]×(MaxPre[i, R]-Pre[i-1] + MaxSuf[L,i]-Suf[i])
    因为 a [ i ] 0 a[i] \ge0 所以我们在区间 [ i , R ] [i, R] 找到一个最大的前缀和, 在区间 [ L , i ] [L, i] 找到一个最大的后缀和这样就能保证值最大

  • a [ i ] < 0 a[i] <0
    和上一种情况相反
    A n s = a [ i ] × ( M i n P r e [ i , R ] P r e [ i 1 ] + M i n S u f [ L , i ] S u f [ i ] ) Ans = a[i]×(MinPre[i, R]-Pre[i-1] + MinSuf[L,i]-Suf[i])
    因为 a [ i ] < 0 a[i] <0 所以我们在区间 [ i , R ] [i, R] 找到一个最小的前缀和, 在区间 [ L , i ] [L, i] 找到一个最小的后缀和这样就能保证值最大

Ac之路很坎坷,刚开始全用ST表维护最大值最小值,结果MLE(ST很快但是空间也很高),然后把求后缀的ST表用线段树写,还是MLE。前后缀都带用线段树写,两个线段树代码重复太高而且变量名也不好起,还没用C++写过类,学习下杰哥用类写的线段树。Orz。。。

AC

#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int maxn = 5e5 + 5;
  
int a[maxn];	 
int dp[maxn][20];
LL pre[maxn], suf[maxn];
void ST(int n) {
	for (int i = 1; i <= n; ++i) {
		dp[i][0] = a[i];
	}
	for (int j = 1; j <= log2(n); ++j) {
		for (int i = 1; i+(1<<j)-1 <= n; ++i) {
			dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
		}
	}
}
LL RMQ(int l, int r) {
	int len = r - l + 1;
	int x = log2(len);
	return min(dp[l][x], dp[r - (1<<x)+1][x]);
}

class Seg{
	LL Max[maxn<<2], Min[maxn<<2];
	public: void build(int rt, int l, int r, LL a[]) {
	 	if (l == r) {
			Max[rt] = a[l];
			Min[rt] = a[l];
			return;
		}
		build(lc, l, mid, a);
		build(rc, mid+1, r, a);
		Max[rt] = max(Max[lc], Max[rc]);
		Min[rt] = min(Min[lc], Min[rc]);	
	}

	public: LL query_min(int rt, int l, int r, int ql, int qr) {
			if (r < ql || l > qr) return 1e18;
			if (l >= ql && r <= qr) return Min[rt];
			LL tmp1 = query_min(lc, l, mid, ql, qr);
			LL tmp2 = query_min(rc, mid+1, r, ql, qr);
			return min(tmp1, tmp2);
	}
	public: LL query_max(int rt, int l, int r, int ql, int qr) {
			if (r < ql || l > qr) return -1e18;
			if (l >= ql && r <= qr) return Max[rt];
			LL tmp1 = query_max(lc, l, mid, ql, qr);
			LL tmp2 = query_max(rc, mid+1, r, ql, qr);
			return max(tmp1, tmp2);
	}		
}Pre, Suf;

int main () {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
  
    int n;
    scanf("%d", &n); 
    for (int i = 1; i <= n; ++i) {
    	scanf("%d", &a[i]);
    	pre[i] = pre[i-1] + a[i];
    }
    for (int i = n; i >= 1; --i) {
    	suf[i] = suf[i+1] + a[i];
    }
    ST(n);
    Pre.build(1, 1, n, pre);
    Suf.build(1, 1, n, suf);
    LL ans = -1e18;
    for (int i = 1; i <= n; ++i) {
    	int left, right, l = i, r = n;
    	while (l <= r) {
    		if (RMQ(l, mid) < a[i]) r = mid - 1;
    		else l = mid + 1;
    	}
    	right = r;

    	l = 1, r = i;
    	while (l <= r) {
    		if (RMQ(mid, r) < a[i]) l = mid + 1;
    		else r = mid - 1;
    	}
    	left = l;
    	if (a[i] >= 0) {
    		LL R = Pre.query_max(1, 1, n, i, right);
    		LL L = Suf.query_max(1, 1, n, left, i);
    		ans = max(ans, (R - pre[i-1] + L - suf[i]) * a[i]);
    		
    	}else {
    		LL R = Pre.query_min(1, 1, n, i, right);
    		LL L = Suf.query_min(1, 1, n, left, i);
    		ans = max(ans, (R - pre[i-1] + L - suf[i]) * a[i]);
    	}
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/henuyh/article/details/89426274
今日推荐