【清华集训 2015】V

前言

    事实证明,一个良好的语文水平是学习的基础。——— Sgzz.ssj.sse.ssl

简要题目

     需 要 你 维 护 长 度 为 n 的 序 列 并 支 持 下 列 操 作 : 需要你维护长度为n的序列并支持下列操作: n
         1. 区 间 加 法 ; 1.区间加法; 1.
         2. 区 间 赋 值 ; 2.区间赋值; 2.
         3. 区 间 每 个 a i 变 成 m a x ( a i − t , 0 ) ; 3.区间每个 ai 变成 max(ai−t,0); 3.aimax(ait,0)
         4. 单 点 询 问 值 ; 4.单点询问值; 4.
         5. 单 点 询 问 历 史 最 大 值 ; 5.单点询问历史最大值; 5.
     n , m ≤ 500000 , 其 中 m 为 操 作 数 。 n,m≤500000,其中 m 为操作数。 n,m500000m

首先我们的思路是:找到区间编号o,若本来区间有标记就计算,下放,再覆盖新的标记。但这里涉及到了一个问题:如果o的儿子也有标记,按照规定需要继续下放,如果儿子的儿子也有呢?

我们发现,这在时间上是行不通的。

然后,就有一个神奇的玩意儿:令每个节点的值为 m a x ( a + x , b ) max(a+x,b) max(a+x,b)

可以分别实现操作1,2,3。

我们还可以证明这是可合并的:合并 ( a , b ) , ( c , d ) − − > ( a + c , m a x ( b + c , d ) ) (a,b),(c,d)-->(a+c,max(b+c,d)) (a,b),(c,d)>(a+c,max(b+c,d))

最后考虑下传标记。易知,父亲的信息必然在儿子的信息之后。

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;

const int N = 5e5 + 2;
const ll lim = (1ll << 62);
int n, m;
ll na[N << 2], nb[N << 2], ha[N << 2], hb[N << 2], ans1, ans2;
bool la[N << 2];

void build(const int o, const int l, const int r) {
	if(l == r) {
		scanf("%lld", &na[o]);
		ha[o] = na[o];
		return;
	}
	int mid = l + r >> 1;
	build(o << 1, l, mid);
	build(o << 1 | 1, mid + 1, r);
}

void pushDown(const int o) {
	if(! la[o]) return;
	int lson = (o << 1), rson = (o << 1 | 1);
	la[lson] = la[rson] = 1;
	ha[lson] = max(ha[lson], ha[o] + na[lson]);
	ha[rson] = max(ha[rson], ha[o] + na[rson]);
	hb[lson] = max(hb[lson], max(hb[o], nb[lson] + ha[o]));
	hb[rson] = max(hb[rson], max(hb[o], nb[rson] + ha[o]));//为什么加ha[o]? 因为下放之后o的标记会清0,此时的ha一定在儿子获得值之后
	nb[lson] = max(nb[o], na[o] + nb[lson]);
	nb[rson] = max(nb[o], na[o] + nb[rson]);
	na[lson] = max(na[lson] + na[o], -lim);
	na[rson] = max(na[rson] + na[o], -lim);
	na[o] = nb[o] = ha[o] = hb[o] = 0;
	la[o] = 0;
}

void add(const int o, const int l, const int r, const int L, const int R, const ll k, const ll Lim) {
	if(l > R || r < L) return;
	if(l >= L && r <= R) {
		na[o] = max(na[o] + k, -lim);
		nb[o] = max(nb[o] + k, Lim);
		ha[o] = max(ha[o], na[o]);
		hb[o] = max(hb[o], nb[o]);
		la[o] = 1;
		return;
	}
	int mid = l + r >> 1;
	pushDown(o);
	add(o << 1, l, mid, L, R, k, Lim);
	add(o << 1 | 1, mid + 1, r, L, R, k, Lim);
}

void ask(const int o, const int l, const int r, const int goal, const bool op) {
	if(l > goal || r < goal) return;
	if(l == r) {
		ans1 = (op == 0 ? na[o] : ha[o]);
		ans2 = (op == 0 ? nb[o] : hb[o]);
		return;
	}
	pushDown(o);
	int mid = l + r >> 1;
	ask(o << 1, l, mid, goal, op);
	ask(o << 1 | 1, mid + 1, r, goal, op);
}

int main() {
	int op, l, r;
	ll x;
	scanf("%d %d", &n, &m);
	build(1, 1, n);
	while(m --) {
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d %d %lld", &l, &r, &x);
			add(1, 1, n, l, r, x, 0);
		}
		else if(op == 2) {
			scanf("%d %d %lld", &l, &r, &x);
			add(1, 1, n, l, r, -x, 0);
		}
		else if(op == 3) {
			scanf("%d %d %lld", &l, &r, &x);
			add(1, 1, n, l, r, -lim, x);
		}
		else if(op == 4) {
			scanf("%lld", &x);
			ask(1, 1, n, x, 0);
			printf("%lld\n", max(ans1, ans2));
		}
		else {
			scanf("%lld", &x);
			ask(1, 1, n, x, 1);
			printf("%lld\n", max(ans1, ans2));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值