维修数列[NOI2005]

好久没写题解了 来水一篇题解

题目描述

请写一个程序,要求维护一个数列,支持以下 6 种操作:

输入格式

输入的第1行包含两个数 \(N\)\(M(M \leq 20 000)\)\(N\) 表示初始时数列中数的个数,\(M\) 表示要进行的操作数目。

第2行包含 \(N\) 个数字,描述初始时的数列。

以下 \(M\) 行,每行一条命令,格式参见问题描述中的表格。

任何时刻数列中最多含有 \(500000\) 个数,数列中任何一个数字均在 \([-1 000, 1 000]\) 内。

插入的数字总数不超过 \(4 000 000\) 个,输入文件大小不超过20MB。

输出格式

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

题解

一道平衡树维护各种标记以及pushup,pushdown的模板题?

我这里用的是飞旋非旋Treap,Splay也可以做,但是显然非旋Treap要比较好写

维护最大子段和的套路就是维护区间最大前缀和以及最大后缀和,pushup时:

最大前缀和 = max(左儿子最大前缀和,左儿子区间和+当前节点权值+右儿子最大前缀和)
最大后缀和 = max(右儿子最大后缀和,右儿子区间和+当前节点权值+左儿子最大后缀和)
最大子段和 = max( max(左儿子最大子段和,右儿子最大子段和),左儿子最大后缀和+当前节点权值+右儿子最大前缀和)

此题细节特别多:

  • 最大子段不能为空 所以修改时要注意不能让它不含任何元素而子段和等于 \(0\) ,尤其是pushup的时候注意

  • 最大前缀和以及最大后缀和可以为空,所以最小也是 \(0\) ,不会为负

  • 覆盖标记的初始值不能设成 \(0\) !因为一次操作可能会要求把一段区间全部赋为 \(0\) ,可以把覆盖标记初始值设成inf

  • 区间翻转的时候,不光要交换左右儿子,还要交换最大前缀和与最大后缀和!

  • 在树的结构即将发生变化时,一定要记得先pushdown!

代码

#include <bits/stdc++.h>
#define N 500005
#define M 4000005
using namespace std;

template<typename T>
inline void read(T &num) {
	T x = 0, f = 1; char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
	num = x * f;
}

int n, m, nowtot;
int rt, tot, siz[M], val[M], rnd[M], ch[M][2], tag[M], rev[M];
int mx[M], sum[M], lmx[M], rmx[M];
const int inf = 0x7fffffff;

inline int newnode(int v) {
	tot++;
	siz[tot] = 1; val[tot] = sum[tot] = v; rnd[tot] = rand(); 
	ch[tot][0] = ch[tot][1] = 0; tag[tot] = inf; rev[tot] = 0;
	lmx[tot] = rmx[tot] = max(0, v); mx[tot] = v;
	return tot;
}

inline void pushup(int x) {
	siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + 1;
	sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x];
	lmx[x] = max(lmx[ch[x][0]], sum[ch[x][0]] + val[x] + lmx[ch[x][1]]);
	rmx[x] = max(rmx[ch[x][1]], sum[ch[x][1]] + val[x] + rmx[ch[x][0]]);
	lmx[x] = max(lmx[x], 0); rmx[x] = max(rmx[x], 0);
	mx[x] = rmx[ch[x][0]] + val[x] + lmx[ch[x][1]];
	if (ch[x][0]) mx[x] = max(mx[x], mx[ch[x][0]]);	
	if (ch[x][1]) mx[x] = max(mx[x], mx[ch[x][1]]);	
}

inline void change(int x, int v) {
	val[x] = v; 
	sum[x] = siz[x] * v;
	lmx[x] = rmx[x] = max(0, siz[x] * v); 
	mx[x] = max(v, siz[x] * v);
	tag[x] = v;
}

inline void Rev(int x) {
	swap(lmx[x], rmx[x]);
	swap(ch[x][0], ch[x][1]);
	rev[x] ^= 1;
}

inline void pushdown(int x) {
	if (tag[x] != inf) {
		int v = tag[x]; tag[x] = inf;
		if (ch[x][0]) change(ch[x][0], v);
		if (ch[x][1]) change(ch[x][1], v);
	}
	if (rev[x]) {
		if (ch[x][0]) Rev(ch[x][0]); 
		if (ch[x][1]) Rev(ch[x][1]); 
		rev[x] = 0;
	}
}

void split(int now, int k, int &x, int &y) {
	if (!now) {
		x = y = 0; return;
	}
	pushdown(now);
	if (k > siz[ch[now][0]]) {
		x = now; split(ch[now][1], k - siz[ch[now][0]] - 1, ch[now][1], y);
	} else {
		y = now; split(ch[now][0], k, x, ch[now][0]);
	}
	pushup(now);
}

int merge(int x, int y) {
	if (!x || !y) return x | y;
	pushdown(x); pushdown(y); 
	if (rnd[x] < rnd[y]) {
		ch[x][1] = merge(ch[x][1], y);
		pushup(x);
		return x;
	} else {
		ch[y][0] = merge(x, ch[y][0]);
		pushup(y);
		return y; 
	}
}

char s[50]; 

int main() {
	srand(time(NULL));
	read(n); read(m);
	for (int i = 1, v, x, y; i <= n; i++) {
		read(v); x = y = 0;
		split(rt, i - 1, x, y);
		rt = merge(merge(x, newnode(v)), y);
	}
	nowtot = n;
	for (int i = 1; i <= m; i++) {
		scanf("%s", s);
		int pos = 0, cnt = 0, v = 0, x = 0, y = 0, z = 0;
		if (s[0] == 'I') {
			read(pos); read(cnt);
			nowtot += cnt;
			split(rt, pos, x, y);
			for (int j = 1; j <= cnt; j++) {
				read(v); 
				x = merge(x, newnode(v));
			}
			rt = merge(x, y);
		} else if (s[0] == 'D') {
			read(pos); read(cnt);
			nowtot -= cnt;
			split(rt, pos - 1, x, y);
			split(y, cnt, y, z);
			rt = merge(x, z);
		} else if (s[0] == 'R') {
			read(pos); read(cnt);
			split(rt, pos - 1, x, y);
			split(y, cnt, y, z);
			Rev(y);
			rt = merge(x, merge(y, z));
		} else if (s[0] == 'G') {
			read(pos); read(cnt);
			split(rt, pos - 1, x, y);
			split(y, cnt, y, z);
			printf("%d\n", sum[y]);
			rt = merge(x, merge(y, z));
		} else if (s[2] == 'K') {
			read(pos); read(cnt); read(v);
			split(rt, pos - 1, x, y);
			split(y, cnt, y, z);
			change(y, v);
			rt = merge(x, merge(y, z));
		} else {
			printf("%d\n", mx[rt]);
		}
	}
	return 0;
} 

猜你喜欢

转载自www.cnblogs.com/ak-dream/p/AK_DREAM91.html