• Codeforces 1418D


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


    题意

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

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

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

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

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

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

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


    限制

    (1leq n,qleq 10^5)

    (1leq p_ileq 10^9)

    (0leq t_ileq 1, 1leq x_ileq 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
    ";
            return;
        }
        cout<<*(--st.end())-*(st.begin())-*(--st2.end())<<'
    '; //两端数字差值-相邻最大差值
    }
    
    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;
    }
    
    

  • 相关阅读:
    文件内容排名算法,输入排名函数,返回排名后的文件名
    线段树做大数据排序
    给字符排序-基类排序二分查找-JavaScript
    后缀数组、名次数组-JavaScript
    二分查找法、二分去重排序法,返回最接近的位置和实际位置
    用四叉树对图像分类,获取tag和key
    Linux显示所在Git分支
    Linux中设置Git显示颜色
    屏蔽网页广告
    tf.add_to_collection,tf.get_collection简介
  • 原文地址:https://www.cnblogs.com/stelayuri/p/13708968.html
Copyright © 2020-2023  润新知