Educational Codeforces Round 95 (Rated for Div. 2)D. Trash Problem(权值线段树+离散化)

题目描述

Vova decided to clean his room. The room can be represented as the coordinate axis OX. There are n piles of trash in the room, coordinate of the i-th pile is the integer pi. All piles have different coordinates.
Let’s define a total cleanup as the following process. The goal of this process is to collect all the piles in no more than two different x coordinates. To achieve this goal, Vova can do several (possibly, zero) moves. During one move, he can choose some x and move all piles from x to x+1 or x−1 using his broom. Note that he can’t choose how many piles he will move.
Also, there are two types of queries:
0 x — remove a pile of trash from the coordinate x. It is guaranteed that there is a pile in the coordinate x at this moment.
1 x — add a pile of trash to the coordinate x. It is guaranteed that there is no pile in the coordinate x at this moment.
Note that it is possible that there are zero piles of trash in the room at some moment.
Vova wants to know the minimum number of moves he can spend if he wants to do a total cleanup before any queries. He also wants to know this number of moves after applying each query. Queries are applied in the given order. Note that the total cleanup doesn’t actually happen and doesn’t change the state of piles. It is only used to calculate the number of moves.
For better understanding, please read the Notes section below to see an explanation for the first example.

Input

The first line of the input contains two integers n and q (1≤n,q≤105) — the number of piles in the room before all queries and the number of queries, respectively.
The second line of the input contains n distinct integers p1,p2,…,pn (1≤pi≤109), where pi is the coordinate of the i-th pile.
The next q lines describe queries. The i-th query is described with two integers ti and xi (0≤ti≤1;1≤xi≤109), where ti is 0 if you need to remove a pile from the coordinate xi and is 1 if you need to add a pile to the coordinate xi. It is guaranteed that for ti=0 there is such pile in the current set of piles and for ti=1 there is no such pile in the current set of piles.

Output

Print q+1 integers: the minimum number of moves Vova needs to do a total cleanup before the first query and after each of q queries.

Examples

input
5 6
1 2 6 8 10
1 4
1 9
0 6
0 10
1 100
1 50
output
5
7
7
5
4
8
49
input
5 8
5 1 2 4 3
0 1
0 2
0 3
0 4
0 5
1 1000000000
1 1
1 500000000
output
3
2
1
0
0
0
0
0
499999999

Note

Consider the first example.
Initially, the set of piles is [1,2,6,8,10]. The answer before the first query is 5 because you can move all piles from 1 to 2 with one move, all piles from 10 to 8 with 2 moves and all piles from 6 to 8 with 2 moves.
After the first query, the set becomes [1,2,4,6,8,10]. Then the answer is 7 because you can move all piles from 6 to 4 with 2 moves, all piles from 4 to 2 with 2 moves, all piles from 2 to 1 with 1 move and all piles from 10 to 8 with 2 moves.
After the second query, the set of piles becomes [1,2,4,6,8,9,10] and the answer is the same (and the previous sequence of moves can be applied to the current set of piles).
After the third query, the set of piles becomes [1,2,4,8,9,10] and the answer is 5 because you can move all piles from 1 to 2 with 1 move, all piles from 2 to 4 with 2 moves, all piles from 10 to 9 with 1 move and all piles from 9 to 8 with 1 move.
After the fourth query, the set becomes [1,2,4,8,9] and the answer is almost the same (the previous sequence of moves can be applied without moving piles from 10).
After the fifth query, the set becomes [1,2,4,8,9,100]. You can move all piles from 1 and further to 9 and keep 100 at its place. So the answer is 8.
After the sixth query, the set becomes [1,2,4,8,9,50,100]. The answer is 49 and can be obtained with almost the same sequence of moves as after the previous query. The only difference is that you need to move all piles from 50 to 9 too.

题目大意

有n堆垃圾,每一堆的坐标为x,合并两堆垃圾的花费为这两堆垃圾的坐标之差的绝对值。
你可以进行q次操作。1 x表示在x位置上增加一堆垃圾,0 x表示删除x位置上的垃圾。
输出将这n堆垃圾合并成两堆垃圾所需的花费,以及每次操作之后改变的答案。

题目分析

我们可以发现,每次合并垃圾之后,所有相邻的垃圾的坐标差去掉最大值之后的和即为此次合并的最小花费。
但因为有q次操作,因此不能直接用差分来完成。我们可以用权值线段树来做。
先将所有需要用的数进行一个离散化,再用这个离散化之后的数组建树。
因为最开始的时候,不是所有数都加入到了序列中,因此有一些数最开始是没有的。我们可以用一个标记数组st[i],st[i]=true才表示第i个数在当前的序列中。
线段树中我们需要维护的是:相邻的两个有效数的差的最大值max。u段的max等于 两个子段中的max 和 右子段的左端点值(最大值)-左子段的右端点值(最小值)的最大值。因此我们还需要维护两个额外信息:这一段中的最大值ma和最小值mi。
剩下的就是直接套线段树模板即可了。
注意:在pushup操作中要注意不要让st[i]=false的点的信息去更新父段信息。

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=2e5+5,INF=0x3f3f3f3f;
struct Node{
	int mi,ma;	//每一段的最小值、最大值
	int max;	//这一段中相邻两个数的差的最大值
}tr[N*4];
int a[N],b[N];
bool st[N];		//标记每个点十分在当前序列中
int op[N],q[N];	//记录q次操作的询问(离线算法)
void pushup(int u)			//注意不要让st[i]=0的点去跟新信息
{
	tr[u].ma=max(tr[u<<1].ma,tr[u<<1|1].ma);
	
	if(tr[u<<1].mi==0||tr[u<<1|1].mi==0) tr[u].mi=max(tr[u<<1].mi,tr[u<<1|1].mi);
	else tr[u].mi=min(tr[u<<1].mi,tr[u<<1|1].mi);
	
	if(tr[u<<1].ma==0||tr[u<<1|1].mi==0) tr[u].max=max(tr[u<<1].max,tr[u<<1|1].max);
	else tr[u].max=max(max(tr[u<<1].max,tr[u<<1|1].max),tr[u<<1|1].mi-tr[u<<1].ma);
}
void build(int u,int l,int r)
{
	if(l==r)
	{
		if(st[l]) tr[u]={a[l],a[l],0};		//因为此段只有一个点,因此max=0
		else tr[u]={0,0,0};					//st[i]=0的点统一赋成 0 0 0
	}
	else
	{
		int mid=l+r>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	}
}
void modify(int u,int l,int r,int x,bool op)
{
	if(l==r)
	{
		if(op) tr[u]={a[l],a[l],0};		//op=1,加入一个数
		else tr[u]={0,0,0};				//op=0,删除一个数
	}
	else
	{
		int mid=l+r>>1;
		if(x<=mid) modify(u<<1,l,mid,x,op);
		else modify(u<<1|1,mid+1,r,x,op);
		pushup(u); 
	}
}
int query()		//最小合并费用即为整段的相邻数的差(最大值-最小值)-相邻数差的最大值max
{
	return tr[1].ma-tr[1].mi-tr[1].max;
}
int main()
{
	int n,Q;
	scanf("%d %d",&n,&Q);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];				//b[]备份初始的n个数
	}
	for(int i=1;i<=Q;i++)		//将所有需要用到的坐标放入a[]中
	{
		scanf("%d %d",&op[i],&q[i]);
		a[i+n]=q[i];
	}
	sort(b+1,b+1+n);
	sort(a+1,a+1+n+Q);					//离散化
	int cnt=unique(a+1,a+1+n+Q)-a-1;
	for(int i=1;i<=Q;i++)
		q[i]=lower_bound(a+1,a+1+cnt,q[i])-a;
	
	int k=1;
	for(int i=1;i<=n;i++)		//将初始的n个数进行标记
	{
		while(b[i]!=a[k]) k++;
		st[k]=true;
	}
	build(1,1,cnt);				//建树
	printf("%d\n",query());		//查询
	for(int i=1;i<=Q;i++)
	{
		modify(1,1,cnt,q[i],op[i]);
		printf("%d\n",query());
	}
	return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值