D. Trash Problem 线段树

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\n",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\n",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\n",ans);
    }
    return 0;
}
posted @ 2021-04-08 16:18  EchoZQN  阅读(35)  评论(0编辑  收藏  举报