[LOJ 6109]「2017 山东二轮集训 Day4」增添

LOJ传送门

题目描述

有一个长度为 n n n 的序列,要求支持三种操作:

  • 1 l r x [ l , r ] [l, r] [l,r] 中的数增加 x x x,保证 x ≤ 10000 x \leq 10000 x10000
  • 2 l r x [ l , l + x ] [l, l + x] [l,l+x]中的数对应替换 $[r, r + x] $中的数;
  • 3 l r [ l , r ] [l, r] [l,r] 中所有数的和。

输入输出格式

输入格式

第一行两个正整数 n , m n, m n,m表示序列长度和操作数。
第二行 $n $个正整数表示初始序列中的数,保证每个数 ≤ 10000 \leq 10000 10000
接下来 m m m 行,每行三或四个整数,对应一个操作。
保证操作的区间合法且为 [ 1 , n ] [1, n] [1,n] 的子集。

输出格式

对每个操作三,单独输出一行表示答案。

输入输出样例

输入样例#1:
4 4
1 2 3 4
1 2 3 4
3 1 4
2 1 3 1
3 2 4
输出样例#1:
18
13

数据范围与提示

n ≤ 100000 , m ≤ 100000 n \leq 100000, m \leq 100000 n100000,m100000

解题分析

区间替换这个操作, 一开始没什么头绪, 想了一会发现: 诶这不就和平衡树的插入差不多吗, 把原来一截 e r a s e erase erase掉然后 i n s e r t insert insert一截新的。 只不过这段新的是原来序列的一部分, 那就可持久化一下就好了。

R o c k d u Rockdu Rockdu大佬学了一下 f h q T r e a p fhqTreap fhqTreap不记 k e y key key值的写法, 因为实在卡空间…

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define gc getchar()
#define MX 100500
#define ll long long
template <class T>
IN void in(T &x)
{
	static char c; static bool neg;
	x = 0; c = gc;
	for (; !isdigit(c); c = gc)
	if (c == '-') neg = true;
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
	if (neg) neg = false, x = -x;
}
int cnt, n, m, root;
struct Node {int son[2], val, siz, tag; ll sum;} tree[MX * 350];
IN int rd() {return (1ll * rand() * rand() % INT_MAX + rand()) % INT_MAX;}
IN void pushup(R int now)
{
	tree[now].siz = tree[ls].siz + tree[rs].siz + 1;
	tree[now].sum = tree[ls].sum + tree[rs].sum + tree[now].val;
}
IN void pushdown(R int now)
{
	if (tree[now].tag)
	{
		if (ls)
		{
			tree[++cnt] = tree[ls], ls = cnt;
			tree[ls].tag += tree[now].tag;
			tree[ls].sum += 1ll * tree[now].tag * tree[ls].siz;
			tree[ls].val += tree[now].tag;
		}
		if (rs)
		{
			tree[++cnt] = tree[rs], rs = cnt;
			tree[rs].tag += tree[now].tag;
			tree[rs].sum += 1ll * tree[now].tag * tree[rs].siz;
			tree[rs].val += tree[now].tag;
		}
		tree[now].tag = 0;
	}
}
void split(R int now, R int tar, int &x, int &y)
{
	if (!now) return x = y = 0, void();
	pushdown(now);
	if (tree[ls].siz >= tar)
	{
		tree[y = ++cnt] = tree[now];
		split(ls, tar, x, tree[y].son[0]);
		pushup(y);
	}
	else
	{
		tree[x = ++cnt] = tree[now];
		split(rs, tar - tree[ls].siz - 1, tree[x].son[1], y);
		pushup(x);
	}
}
int Merge(R int x, R int y)//normal, when inserting
{
	if ((!x) || (!y)) return x | y;
	if (rd() % (tree[x].siz + tree[y].siz) < tree[x].siz)
	{
		pushdown(x);
		tree[x].son[1] = Merge(tree[x].son[1], y);
		pushup(x); return x;
	}
	else
	{
		pushdown(y);
		tree[y].son[0] = Merge(x, tree[y].son[0]);
		pushup(y); return y;
	}
}
int merge(R int x, R int y)
{
	if ((!x) || (!y)) return x | y;
	if (rd() % (tree[x].siz + tree[y].siz) < tree[x].siz)
	{
		pushdown(x);
		tree[++cnt] = tree[x], x = cnt;
		tree[x].son[1] = merge(tree[x].son[1], y);
		pushup(x); return x;
	}
	else
	{
		pushdown(y);
		tree[++cnt] = tree[y], y = cnt;
		tree[y].son[0] = merge(x, tree[y].son[0]);
		pushup(y); return y;
	}
}
IN void query(R int lef, R int rig)
{
	R int x, y, z;
	split(root, rig, x, z);
	split(x, lef - 1, x, y);
	printf("%lld\n", tree[y].sum);
}
IN void add(R int lef, R int rig, R int tg)
{
	R int x, y, z;
	split(root, rig, x, z);
	split(x, lef - 1, x, y);
	tree[y].tag += tg;
	tree[y].sum += tree[y].siz * tg;
	tree[y].val += tg;
	root = Merge(Merge(x, y), z);
}
IN void modify(R int from, R int to, R int len)
{
	int x, y, z, p, q, w;
	split(root, from + len, x, z);
	split(x, from - 1, x, y);
	split(root, to + len, p, w);
	split(p, to - 1, p, q);
	root = merge(merge(p, y), w);
}
int main(void)
{
	int typ, l, r, x;
	srand(time(NULL));
	in(n), in(m);
	for (R int i = 1; i <= n; ++i)
	{
		in(x);
		tree[++cnt] = {{0, 0}, x, 1, 0, x};
		root = Merge(root, cnt);
	}
	W (m--)
	{
		in(typ);
		switch(typ)
		{
			case 1: in(l), in(r), in(x), add(l, r, x); break;
			case 2: in(l), in(r), in(x), modify(l, r, x); break;
			case 3: in(l), in(r), query(l, r); break;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值