D. Trash Problem 线段树
题目大意:
有 (n) 堆物品,第 (i) 堆物品在位置 (p_i),每次你可以选择移动一堆物品到另一堆,代价是他们距离的绝对值,问最少的代价使得最后只剩下两堆物品。你有两种操作,每次操作之后输出最小的代价。
- 0 x 表示把 (x) 这个位置的物品移除,保证 (x) 这个位置有物品
- 1 x 表示把 (x) 这个位置加上物品,保证 (x) 这个位置此时没有物品
题解:
- 很容易发现,最后答案就是 最大位置与最小位置的绝对值 减去 相邻区间绝对值最大的这个值。
- 然后这些都可以用线段树维护,最大值最小值就是很普通的线段树。
- 线段树在往上传递的过程 维护相邻区间绝对值最大即可
#include <bits/stdc++.h>
#define lson (id<<1)
#define rson (id<<1|1)
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 2e5 + 10;
typedef long long ll;
int p[maxn],a[maxn],t[maxn];
int v[maxn<<2],maxs[maxn<<2],Max[maxn<<2],Min[maxn<<2];
void push_up(int id) {
Min[id] = min(Min[lson], Min[rson]);
Max[id] = max(Max[lson], Max[rson]);
maxs[id] = max(maxs[lson], maxs[rson]);
if (Min[rson] < inf && Max[lson] > 0) maxs[id] = max(maxs[id], Min[rson] - Max[lson]);
}
void update(int id,int l,int r,int pos,int f){
// printf("update:id = %d l = %d r = %d pos = %d f = %d
",id,l,r,pos,f);
if(l==r){
if(f) Max[id] = Min[id] = v[l];
else Max[id] = 0,Min[id] = inf;
maxs[id] = 0;
return ;
}
int mid = (l+r)>>1;
if(pos<=mid) update(lson,l,mid,pos,f);
else update(rson,mid+1,r,pos,f);
push_up(id);
}
int main(){
int N,M,now = 0;
scanf("%d%d",&N,&M);
memset(Min,inf,sizeof(Min));
for(int i=1;i<=N;i++) scanf("%d",&p[i]),v[++now] = p[i];
for(int i=1;i<=M;i++) scanf("%d%d",&t[i],&a[i]),v[++now] = a[i];
sort(v+1,v+1+now);
now = unique(v+1,v+1+now) - v - 1;
for(int i=1;i<=N;i++){
int pos = lower_bound(v+1,v+1+now,p[i]) - v;
update(1,1,now,pos,1);
}
int ans = max(0,Max[1] - Min[1] - maxs[1]);
printf("%d
",ans);
for(int i=1;i<=M;i++){
int pos = lower_bound(v+1,v+1+now,a[i]) - v;
update(1,1,now,pos,t[i]);
ans = max(0,Max[1] - Min[1] - maxs[1]);
printf("%d
",ans);
}
return 0;
}