LG-P7549 [BJWC2017] 神秘物质 题解

9 篇文章 0 订阅

传送门:P7549 [BJWC2017] 神秘物质

先膜拜一下 cyf \text{cyf} cyf 学长。本题解主要是补充一下 cyf \text{cyf} cyf 学长思路的细节。

Solution

1

首先看操作一、二,发现它们的本质实际上都是对一段区间的操作。

所以本题使用文艺平衡树来维护。

也就是说,对于每次的一、二操作,我们都会将要进行修改的区间旋转到一颗子树内,即文艺平衡树中翻转区间的预操作。

具体可以看@皎月半洒花在文艺平衡树题解中放的这张图:(设将要修改的区间为 [ l , r ] [l,r] [l,r]。)

不同之处在于我们后面并不是执行翻转操作。

不过在这道题中要注意的是:

因为我们在最开始建树的时候,为了防止越界,在序列开头和结尾各新增了一个节点,所以此时我们要修改的区间就变成了 [ l + 1 , r + 1 ] [l + 1, r + 1] [l+1,r+1],所以需要查找和被执行旋转操作的节点就变成了序列中第 l l l 个节点和第 ( r + 2 ) (r + 2) (r+2) 个节点。

2

再来看三、四操作。

第三个操作显然就是给每个节点都维护一个其区间内的最大值和最小值,查询的时候直接将区间 [ l , r ] [l,r] [l,r] 按 1 中所述的那样旋转到一起去,然后输出最大值和最小值的差值即可。

重点在于第四个操作。

可以发现一个性质:对于一个区间 [ l , r ] [l,r] [l,r],新增加一个数并不会使其区间极差最小值更小。

证明:

区间 [ l , r ] [l,r] [l,r] 的极差最小值是其中的最大值和最小值之差。而对于新增节点来说,可能可以造成影响的情况就是它会更新此区间的最大(小)值。

但是,我们发现不论它是更新了最大值还是最小值,都只会让这个区间极差更大(这个不难理解)。

证毕。

所以我们实际上只需要给每个节点都维护与其相邻节点的差值就好了,同时还要维护一下这个节点所代表的区间内任意子区间中区间极差的最小值。

查询的时候注意要判断查询区间仅有两个节点的情况。

Code

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define ls t[x].ch[0]
#define rs t[x].ch[1]
const int maxn = 3e5 + 10;
const int inf = 2147483647;
int n, m;
int rt, tot;
int e[maxn];
struct node{
	int ch[2];
	int siz, f, val;
	int mn, mx;
	int lb, rb, ms;
}t[maxn];

inline void up(int x)
{
	t[x].siz = t[ls].siz + 1 + t[rs].siz;
	t[x].mn = t[x].mx = t[x].val;
	t[x].ms = min(t[x].lb, t[x].rb);
	if(ls) 
		t[x].mn = min(t[x].mn, t[ls].mn), t[x].mx = max(t[x].mx, t[ls].mx), 
		t[x].ms = min(t[x].ms, t[ls].ms);
	if(rs) 
		t[x].mn = min(t[x].mn, t[rs].mn), t[x].mx = max(t[x].mx, t[rs].mx), 
		t[x].ms = min(t[x].ms, t[rs].ms);
}

inline int nw(int v)
{
	int u = ++tot;
	t[u].mn = t[u].mx = t[u].val = v;
	return u;
}

inline void rotate(int x)
{
	int y = t[x].f, z = t[y].f;
	int kx = (t[y].ch[1] == x), ky = (t[z].ch[1] == y);
	t[z].ch[ky] = x, t[x].f = z;
	t[y].ch[kx] = t[x].ch[kx ^ 1], t[t[x].ch[kx ^ 1]].f = y;
	t[x].ch[kx ^ 1] = y, t[y].f = x;
	up(y), up(x);
}

inline void splay(int x, int gl)
{
	while(t[x].f != gl)
	{
		int y = t[x].f, z = t[y].f;
		if(z != gl) 
			(t[y].ch[1] == x) ^ (t[z].ch[1] == y) ? rotate(x) : rotate(y);
		rotate(x);
	}
	if(!gl) rt = x;
}

inline int build(int l, int r, int fa)
{
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	int u = ++tot;
	t[u].f = fa, t[u].val = e[mid];
	t[u].lb = mid ? abs(e[mid] - e[mid - 1]) : inf;
	t[u].rb = mid != n + 1 ? abs(e[mid] - e[mid + 1]) : inf;
	t[u].ch[0] = build(l, mid - 1, u);
	t[u].ch[1] = build(mid + 1, r, u);
	up(u);
	return u;
}

inline int find(int k)
{
	int x = rt;
	while(531)
	{
		if(t[ls].siz + 1 < k)
			k -= t[ls].siz + 1, x = rs;
		else if(t[ls].siz >= k)
			x = ls;
		else return x;
	}
}

inline int split(int l, int r)
{
	int x = find(l), y = find(r + 2);
	splay(x, 0), splay(y, x);
	return t[y].ch[0];
}

inline void delet(int x)
{
	ls = rs = t[x].f = t[x].siz = t[x].val = t[x].mx = 0;
	t[x].mn = t[x].ms = t[x].lb = t[x].rb = inf;
}

inline void merge_(int pos, int v)
{
	int x = split(pos, pos + 1), lst = rt, nxt = t[lst].ch[1];
	t[x].val = v;
	if(ls) delet(ls), t[x].ch[0] = 0;
	if(rs) delet(rs), t[x].ch[1] = 0;
	t[x].lb = t[lst].rb = abs(v - t[lst].val);
	t[x].rb = t[nxt].lb = abs(t[nxt].val - v);
	up(x), up(nxt), up(lst);
}

inline void insrt_(int pos, int v)
{
	int x = split(pos, pos), lst = rt, nxt = t[lst].ch[1];
	int u = nw(v);
	t[u].f = x, t[x].ch[1] = u;
	t[x].rb = t[u].lb = abs(v - t[x].val);
	t[nxt].lb = t[u].rb = abs(v - t[nxt].val);
	up(u), up(x), up(nxt), up(lst);
}

inline int qrymx_(int l, int r)
{
	int x = split(l, r);
	return t[x].mx - t[x].mn;
}

inline int qrymn_(int l, int r)
{
	if(l + 1 == r) return t[split(l, r - 1)].rb;
	else return t[split(l + 1, r - 1)].ms;
}

int main()
{
	scanf("%d%d", &n, &m);
	rep(i, 1, n) scanf("%d", &e[i]);
	e[0] = e[n + 1] = inf, tot = n + 1;
	rt = build(0, n + 1, 0);
	rep(i, 1, m)
	{
		char c[10];
		int x, y;
		scanf("%s%d%d", c, &x, &y);
		if(c[1] == 'e') merge_(x, y);         
		if(c[1] == 'n') insrt_(x, y);
		if(c[1] == 'a') printf("%d\n", qrymx_(x, y));
		if(c[1] == 'i') printf("%d\n", qrymn_(x, y));
	}
	return 0;
}

感谢阅读。

辛苦管理员审核,若有问题烦请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值