「Luogu P2042」「Noi2005」维护数列

Description

请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)

\(N\) 表示初始时数列中数的个数,\(M\) 表示要进行的操作数目。

Hint

任何时刻数列中最多含有 \(5\times 10^5\) 个数。

任何时刻数列中任何一个数字均在 \([-10^3,10^3]\) 内。

\(1\le M\le 2\times 10^4\) ,插入的数字总数不超过 \(4\times 10^6\)

Solution

配合食用: 【知识总结】Splay 简单区间操作的一些套路

这个可以说是 Splay 维护数列的板题了。

首先建树,加入虚拟结点。

然后,一个个来。

INSERT 操作

找到要插入的位置左边、右边的两个结点(注意有虚拟结点,需要 +1)。

然后两个结点上旋。

将插入数列建好树,之后在根的右儿子的左儿子的位置处插入建好的树。

最后 maintain 一下。

DELETE 操作

找到待删除数列的端点的 左/右一个位置 的结点,上旋。

使用一个垃圾回收机制,存下删掉的结点,等到重新建树时用。

ch[r][0] ← 0,maintain。

MAKE-SAME 操作

找到待修改区间的端点的 左/右一个位置 的结点,上旋。

打一个标记,maintain。

REVERSE 操作

找到待修改区间的端点的 左/右一个位置 的结点,上旋。

打一个标记,maintain。

GET-SUM 操作

维护一个子树值和。

找到目标区间的端点的 左/右一个位置 的结点,上旋。

获取 sum 返回。

MAX-SUM 操作

有点麻烦,套路就是 GSS 的差不多。

维护一个最大前缀和,最大后缀和,最大子段和。

具体见代码。

坑点

最大子段和的子段必须至少选一个元素。

题目给的参数意义有点区别。

先将 0 号结点初始化一下。

Code

#include <iostream>
#include <algorithm>
#include <string>
#include <stack>
using namespace std;

const int N = 5e5 + 5;
const long long inf = 1e18;
typedef long long LL;

int ch[N][2], fa[N], size[N];
LL val[N], sum[N], lmax[N], rmax[N], mmax[N];
bool cov[N], rev[N];

LL ary[N];
int n, q, total = 0, root;
stack<int> rec;

inline int create() {
	if (rec.empty()) return ++total;
	int ret = rec.top();
	return rec.pop(), ret;
}
inline void clear(int rt) {
	ch[rt][0] = ch[rt][1] = fa[rt] = 0;
	val[rt] = lmax[rt] = rmax[rt] = 0;
	mmax[rt] = -inf;
	cov[rt] = rev[rt] = 0;
}

inline void maintain(int rt) {
	size[rt] = size[ch[rt][0]] + size[ch[rt][1]] + 1;
	sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]] + val[rt];
	lmax[rt] = max(lmax[ch[rt][0]], lmax[ch[rt][1]] + sum[ch[rt][0]] + val[rt]);
	rmax[rt] = max(rmax[ch[rt][1]], rmax[ch[rt][0]] + sum[ch[rt][1]] + val[rt]);
	mmax[rt] = max(max(mmax[ch[rt][0]], mmax[ch[rt][1]]), lmax[ch[rt][1]] + rmax[ch[rt][0]] + val[rt]);
}
inline void pushdown(int rt) {
	int &l = ch[rt][0], &r = ch[rt][1];
	if (cov[rt]) {
		rev[rt] = cov[rt] = 0;
		if (l) cov[l] = 1, val[l] = val[rt], sum[l] = val[rt] * size[l];
		if (r) cov[r] = 1, val[r] = val[rt], sum[r] = val[rt] * size[r];
		if (val[rt] >= 0) {
			if (l) lmax[l] = rmax[l] = mmax[l] = sum[l];
			if (r) lmax[r] = rmax[r] = mmax[r] = sum[r];
		} else {
			if (l) lmax[l] = rmax[l] = 0, mmax[l] = val[l];
			if (r) lmax[r] = rmax[r] = 0, mmax[r] = val[r];
		}
	}
	if (rev[rt]) {
		rev[rt] = 0;
		if (l) rev[l] ^= 1;
		if (r) rev[r] ^= 1;
		swap(lmax[l], rmax[l]);
		swap(lmax[r], rmax[r]);
		swap(ch[l][0], ch[l][1]);
		swap(ch[r][0], ch[r][1]);
	}
}

inline int get(int rt) {
	return rt == ch[fa[rt]][1];
}
inline void rotate(int x) {
	int y = fa[x], z = fa[y], c = get(x);
	ch[y][c] = ch[x][c ^ 1];
	fa[ch[x][c ^ 1]] = y;
	ch[x][c ^ 1] = y;
	fa[y] = x, fa[x] = z;
	if (z) ch[z][y == ch[z][1]] = x;
	maintain(y), maintain(x);
}
inline void splay(int x,int g) {
	for (register int f = fa[x]; (f = fa[x]) != g; rotate(x))
		if(fa[f] != g) rotate(get(f) == get(x) ? f : x);
	if (!g) root = x;
}

inline int select(int k) {
	for (register int rt = root; rt;) {
		pushdown(rt);
		if (k == size[ch[rt][0]] + 1) return rt;
		if (k < size[ch[rt][0]] + 1) rt = ch[rt][0];
		else k -= size[ch[rt][0]] + 1, rt = ch[rt][1];
	}
	throw;
}
int build(int l, int r, int f) {
	if (l>r) return 0;
	int rt = create(), mid = (l + r) >> 1;
	if (l == r) {
		val[rt] = sum[rt] = ary[mid];
		size[rt] = 1;
		ch[rt][0] = ch[rt][1] = 0;
		fa[rt] = f;
		lmax[rt] = rmax[rt] = max(0ll, val[rt]);
		mmax[rt] = val[rt];
		return rt;
	}
	val[rt] = ary[mid], fa[rt] = f;
	ch[rt][0] = build(l, mid - 1, rt);
	ch[rt][1] = build(mid + 1, r, rt);
	return maintain(rt), rt;
}

inline void insert(int pos, int tot) {
	if (!tot) return;
	int l = select(pos + 1), r = select(pos + 2);
	splay(l, 0), splay(r, l);
	ch[r][0] = build(1, tot, r);
	maintain(r), maintain(l);
}

void recycle(int rt) {
	if (!rt) return;
	rec.push(rt);
	recycle(ch[rt][0]);
	recycle(ch[rt][1]);
	clear(rt);
}
inline void erase(int pos, int tot) {
	if (!tot) return;
	int l = select(pos), r = select(pos + tot + 1);
	splay(l, 0), splay(r, l);
	recycle(ch[r][0]), ch[r][0] = 0;
	maintain(r), maintain(l);
}

inline void assign(int pos, int tot, LL c) {
	if (!tot) return;
	int l = select(pos), r = select(pos + tot + 1);
	splay(l, 0), splay(r, l);
	int x = ch[r][0];
	val[x] = c, sum[x] = size[x] * c;
	if (c >= 0) lmax[x] = rmax[x] = mmax[x] = sum[x];
	else lmax[x] = rmax[x] = 0, mmax[x] = val[x];
	cov[x] = 1;
	maintain(r), maintain(l);
}

inline void reverse(int pos, int tot) {
	if (!tot) return;
	int l = select(pos), r = select(pos + tot + 1);
	splay(l, 0), splay(r, l);
	int x = ch[r][0];
	if (cov[x]) return;
	swap(ch[x][0], ch[x][1]);
	swap(lmax[x], rmax[x]);
	rev[x] ^= 1;
	maintain(r), maintain(l);
}

inline LL getSum(int pos, int tot) {
	if (!tot) return 0ll;
	int l = select(pos), r = select(pos + tot + 1);
	splay(l, 0), splay(r, l);
	return sum[ch[r][0]];
}
inline LL getMaxSum() {
	int l = select(1), r = select(size[root]);
	splay(l, 0), splay(r, l);
	return mmax[ch[r][0]];
}

signed main() {
	ios::sync_with_stdio(0);
	cin >> n >> q;
	
	for (register int i = 1; i <= n; i++)
		cin >> ary[i + 1];
	ary[1] = ary[n + 2] = -inf;
	
	clear(0);
	root = build(1, n + 2, 0);
	
	while (q--) {
		string cmd;
		int pos, tot;
		LL c;
		
		cin >> cmd;
		if (cmd == "INSERT") {
			cin >> pos >> tot;
			for (register int i = 1; i <= tot; i++)	
				cin >> ary[i];
			insert(pos, tot);
		}
		if (cmd == "DELETE") {
			cin >> pos >> tot;
			erase(pos, tot);
		}
		if (cmd == "MAKE-SAME") {
			cin >> pos >> tot >> c;
			assign(pos, tot, c);
		}
		if (cmd == "REVERSE") {
			cin >> pos >> tot;
			reverse(pos, tot);
		}
		if (cmd == "GET-SUM") {
			cin >> pos >> tot;
			cout << getSum(pos, tot) << endl;
		}
		if (cmd == "MAX-SUM")
			cout << getMaxSum() << endl;
	}
}

猜你喜欢

转载自www.cnblogs.com/-Wallace-/p/12674103.html
今日推荐