[SP1557 GSS2] Can you answer these queries II(离线 + 线段树) | 错题本

文章目录

题目

[SP1557 GSS2] Can you answer these queries II

分析

思路很巧妙。
先离线所有询问,然后考虑从左到右依次加入每个数,然后考虑如何求目前一个区间的最大子段和:子段无非是两个端点,我们可以用一个数组维护其中一个端点确定时,另一个端点的最优位置,即 a i a_i ai 表示目前以 i i i 为左端点的子段和的最大值,由于是动态加入的,所以 a i a_i ai 实际记录的就是从 i i i 到“当前位置”的子段和的历史最大值。然后我们要知道的是左端点滑动时的子段和最大值,于是用线段树维护 a a a 数组的区间最大值即可!又因为是动态加入的, a a a 数组在区间 [ l , r ] [l, r] [l,r] 的最大值就是以右端点不超过“当前位置”,左端点在 [ l , r ] [l, r] [l,r] 范围内的最大子段和!又由于是动态加入,所以消除重复数字的影响很简单:只修改该数字上一次出现的位置过后的区间内的 a a a 即可。
一次离线,同时解决了两个问题。

代码

实现需要维护当前值、当前值懒标记、历史最大值、历史最大值懒标记,PushDown 也有讲究。

#include <bits/stdc++.h>

int Read() {
	int x = 0; bool f = false; char c = getchar();
	while (c < '0' || c > '9')
		f |= c == '-', c = getchar();
	while (c >= '0' && c <= '9')
		x = x * 10 + (c ^ 48), c = getchar();
	return f ? -x : x;
}

typedef long long LL;

const int MAXN = 100000;
const int MAXQ = 100000;
const int MAXA = 100000;

int N, Q, A[MAXQ + 5];
struct Ask {
	int l, r, i;
} R[MAXQ + 5];

#define lch (u << 1)
#define rch (u << 1 | 1)

struct Node {
	LL cur, his, curtag, histag;

	Node operator + (const Node &other) const {
		return { std::max(cur, other.cur), std::max(his, other.his), 0, 0 };
	}
} T[MAXN * 4 + 5];

inline void PushDown(const int &u) {
	auto Down = [u](Node &c) {
		c.his = std::max(c.his, c.cur + T[u].histag);
		c.histag = std::max(c.histag, c.curtag + T[u].histag);
		c.cur += T[u].curtag, c.curtag += T[u].curtag;
	};
	Down(T[lch]), Down(T[rch]);
	T[u].curtag = T[u].histag = 0;
}

void Add(const int &u, const int &l, const int &r, const int &lft, const int &rgt, const int &val) {
	if (lft <= l && r <= rgt) {
		T[u].his = std::max(T[u].his, T[u].cur += val);
		T[u].histag = std::max(T[u].histag, T[u].curtag += val);
		return;
	}
	PushDown(u);
	int mid = (l + r) >> 1;
	if (lft <= mid) Add(lch, l, mid, lft, rgt, val);
	if (mid + 1 <= rgt) Add(rch, mid + 1, r, lft, rgt, val);
	T[u] = T[lch] + T[rch];
}

Node Query(const int &u, const int &l, const int &r, const int &lft, const int &rgt) {
	if (lft <= l && r <= rgt)
		return T[u];
	PushDown(u);
	int mid = (l + r) >> 1;
	if (rgt <= mid) return Query(lch, l, mid, lft, rgt);
	if (lft >= mid + 1) return Query(rch, mid + 1, r, lft, rgt);
	return Query(lch, l, mid, lft, rgt) + Query(rch, mid + 1, r, lft, rgt);
}

LL Ans[MAXQ + 5];
int Pre[MAXA * 2 + 5];

int main() {
	N = Read();
	for (int i = 1; i <= N; i++)
		A[i] = Read();
	Q = Read();
	for (int i = 1; i <= Q; i++)
		R[i].l = Read(), R[i].r = Read(), R[i].i = i;
	std::sort(R + 1, R + Q + 1, [](const Ask &i, const Ask &j) {
		return i.r < j.r;
	});
	int j = 1;
	for (int i = 1; i <= N; i++) {
		Add(1, 1, N, Pre[A[i] + MAXA] + 1, i, A[i]);
		Pre[A[i] + MAXA] = i;
		while (j <= Q && R[j].r == i)
			Ans[R[j].i] = Query(1, 1, N, R[j].l, R[j].r).his, j++;
	}
	for (int i = 1; i <= Q; i++)
		printf("%lld\n", Ans[i]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值