【Codeforces 1418D】Trash Problem | Set、线段树 、 思维

题目链接:https://codeforces.com/contest/1418/problem/D

题目大意:

给出N堆垃圾,每次可以使得任意一堆向左或者向右移动,如果重合就合并为一堆,问最少需要移动几次使得这些垃圾合并为两堆或者一堆?

题目思路:

首先可以考虑到 ,如果垃圾堆数 > 2,那么肯定是合并为两堆,那么就可以推一下公式,合并为哪两堆

我们可以将垃圾按位置从小到大排序

之后枚举中间点,可以发现会分成a1~ai + a_i+1 ~an 这么两堆

之后推一下公式会发现[L,R]合并成一堆时的权值即为a[R]-a[L]

所以我们可以求出一个公式,要把n堆石子化成两堆即:

a[n] - a[i+1] + a[i] - a[1] 最小

进一步:a[n] - a[1] + a[i] - a[i+1] 最小

所以对于每次询问,我们只需要知道当前的最小值、最大值、最小差值即可

可以用线段树维护,当然为了方便从set写了

Code:


#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC option("arch=native","tune=native","no-zero-upper")
#pragma GCC target("avx2")
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
const ll INF= 1e18;
const int maxn =3e5+7;
const int mod= 998244353;
inline bool read(ll &num)
{char in;bool IsN=false;
    in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
multiset<ll>s1;
set<ll>s2;
ll num[maxn];
unordered_map<ll,int>mp;
int main(){
    read(n);read(m);
    for(int i=1;i<=n;i++) read(num[i]);
    sort(num+1,num+1+n);
    for(int i=1;i<=n;i++){
        s2.insert(num[i]);
        mp[num[i]]++;
        if(i>1)
        s1.insert(num[i-1] - num[i]);
    }
    ll ans = -*(s2.begin())+*prev(s2.end()) + *(s1.begin());
    printf("%lld\n",ans);
    for(int i=1;i<=m;i++){
        ll op,x;read(op);read(x);
        if(op){
            if(!mp[x]){
                auto temp = s2.lower_bound(x);
                if(temp != s2.end()) s1.insert(x - *temp);
                if(temp != s2.begin()) s1.insert(*prev(temp) - x);
                if(temp!=s2.end()&&temp!=s2.begin()) s1.erase(s1.find(*prev(temp) - *temp));
                s2.insert(x);
            }
            mp[x]++;
        }else{
            mp[x]--;
            if(!mp[x]){
                auto tempx = s2.upper_bound(x);
                if(tempx != s2.end()) s1.erase(s1.find(x - *tempx));
                auto tempy = s2.lower_bound(x);
                if(tempy != s2.begin()) s1.erase(s1.find(*prev(tempy) - x));
                if(tempx != s2.end() && tempy != s2.begin()) s1.insert(*prev(tempy) - *tempx);
                s2.erase(x);
            }
        }
        if(s2.size()&&s1.size()) ans = -*(s2.begin())+*prev(s2.end()) + *(s1.begin());
        printf("%lld\n",ans);
    }
    return 0;
}
/***
5 6
1 2 6 8 10
***/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只酷酷光儿( CoolGuang)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值