Codeforces 1418D - Trash Problem (数据结构)

Educational Codeforces Round 95 (Rated for Div. 2) D. Trash Problem


题意

初始时在数轴上有\(n\)个位置\(p\)存在垃圾

每次操作可以将某堆垃圾左移\(1\)个单位或者右移\(1\)个单位

如果两堆垃圾移动到同一位置,它们将会合并成一堆垃圾

问最少的移动次数,使得最后数轴上只剩下不超过两堆垃圾

其后还有\(q\)次操作,每次操作会在某一位置增加一堆垃圾(保证之前没有垃圾)

或者删除某一位置的垃圾(保证之前有垃圾)

每次操作后也要询问一次最少的移动次数


限制

\(1\leq n,q\leq 10^5\)

\(1\leq p_i\leq 10^9\)

\(0\leq t_i\leq 1,\ 1\leq x_i\leq 10^9\)




思路

先不考虑操作,如果直接求最少操作次数

明显得到,可以将所有垃圾分成左右两堆(此时按坐标从小到大排序)

假设分在第\(t\)堆与第\(t+1\)堆之间,那么最小操作次数明显就是\((x_t-x_1)+(x_n-x_{t+1})\)

\((x_n-x_1)-(x_{t+1}-x_t)\)

答案表示的就是此时最远两堆距离减去相邻两堆差值

若要答案最小,由于“最远两堆距离”无法改变,所以应该找出最大的“相邻两堆差值”即可求出答案


所以采取两个multiset

其中一个(st)存目前数轴上垃圾的位置

另外一个(st2)存相邻两堆垃圾的差值

明显,由于set/multiset自动排序,所以答案可以直接通过调用迭代器求出

而对于每一种修改,可以发现——


如果插入一个位置\(x\)

对于st,直接插入一个元素\(x\)即可

对于st2,先对\(x\)进行二分查找,判断其在\(st\)的位置情况

  • 如果\(x\)是最小的元素,那么仅仅是增加一段差值即可(目前最小的元素与\(x\)的差值)
  • 如果\(x\)是最大的元素,那么也仅仅是增加一段差值即可(目前最大的元素与\(x\)的差值)
  • 否则,需要找到插入后与\(x\)相邻的两个元素\(a,b\),删除\(a,b\)的差值,并增加\(a,x\)\(x,b\)的差值

如果删除一个位置\(x\)

对于st,直接删除一个元素\(x\)即可

对于st2,也要先对\(x\)进行二分查找,判断其在\(st\)的位置情况

  • 如果\(x\)是最小的元素,那么仅仅是删除一段差值即可(目前最小的元素与\(x\)的差值)
  • 如果\(x\)是最大的元素,那么也仅仅是删除一段差值即可(目前最大的元素与\(x\)的差值)
  • 否则,需要找到与\(x\)相邻的两个元素\(a,b\),删除\(a,x\)\(x,b\)的差值,并增加\(a,b\)的差值

注意还要对某些特殊情况进行判断(差值集合为\(0\)时等)




程序

(436ms/3000ms)

/*
 *  Author  : StelaYuri
 * Language : GNU G++ 14
 */

//#pragma comment(linker,"/STACK:1024000000,1024000000")
//#pragma GCC optimize(3)
#include<bits/stdc++.h>
//#include<unordered_map>
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll max_3(ll a,ll b,ll c){if(a>b&&a>c)return a;if(b>c)return b;return c;}
ll min_3(ll a,ll b,ll c){if(a<b&&a<c)return a;if(b<c)return b;return c;}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}



int p[100050];
multiset<int> st,st2;

void add(int x)
{
    if(st.size()==0) //插入后只有一个数字,不对st2进行操作
    {
        st.insert(x);
        return;
    }
    auto it=st.lower_bound(x);
    if(it==st.begin()) //x最小,增加一段差值
        st2.insert(*(st.begin())-x);
    else if(it==st.end()) //x最大,增加一段差值
        st2.insert(x-*(--st.end()));
    else //x为中间值,需要删除一段差值并增加两段差值
    {
        st2.erase(st2.find((*it)-(*prev(it))));
        st2.insert((*it)-x);
        st2.insert(x-(*prev(it)));
    }
    st.insert(x);
}

void del(int x)
{
    if(st.size()<=2) //删除后只有少于1个数字,清空st2并不做操作
    {
        st.erase(st.find(x));
        st2.clear();
        return;
    }
    auto it=st.lower_bound(x);
    if(it==st.begin()) //x最小,删除一段差值
        st2.erase(st2.find(*(++it)-x));
    else if(it==--st.end()) //x最大,删除一段差值
        st2.erase(st2.find(x-*(--it)));
    else //x为中间值,需要删除两段差值并增加一段差值
    {
        st2.erase(st2.find((*next(it))-(*it)));
        st2.erase(st2.find((*it)-(*prev(it))));
        st2.insert((*next(it))-(*prev(it)));
    }
    st.erase(st.find(x));
}

void out()
{
    if(st2.empty()) //如果此时差值set为空,直接输出0即可
    {
        cout<<"0\n";
        return;
    }
    cout<<*(--st.end())-*(st.begin())-*(--st2.end())<<'\n'; //两端数字差值-相邻最大差值
}

void solve()
{
    int n,q;
    cin>>n>>q;
    rep(i,1,n)
    {
        cin>>p[i];
        st.insert(p[i]);
    }
    sort(p+1,p+n+1);
    rep(i,2,n)
        st2.insert(p[i]-p[i-1]);
    out();
    while(q--)
    {
        int opr,x;
        cin>>opr>>x;
        if(opr)
            add(x);
        else
            del(x);
        out();
    }
}
int main()
{
    closeSync;
    solve();
    return 0;
}


posted @ 2020-09-21 21:46  StelaYuri  阅读(228)  评论(0编辑  收藏  举报