题目传送门
题目大意
有n堆垃圾,每一堆的坐标为x,合并两堆垃圾的花费为这两堆垃圾的坐标之差的绝对值。
你可以进行q次操作。1 x表示在x位置上增加一堆垃圾,0 x表示删除x位置上的垃圾。
输出将这n堆垃圾合并成两堆垃圾所需的花费,以及每次操作之后改变的答案。
思路
每次合并垃圾之后,所有相邻的垃圾的坐标差去掉最大值之后的和即为此次合并的最小花费
先将所有需要用的数进行一个离散化,再用这个离散化之后的数组建树。
用权值线段树维护:
相邻的两个有效数的差的最大值max。u段的max等于 两个子段中的max 和 右子段的左端点值(最大值)-左子段的右端点值(最小值)的最大值。因此我们还需要维护两个额外信息:这一段中的最大值ma和最小值mi。
AC Code
#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;
}