1. 程式人生 > 實用技巧 >Codeforces 1418D - Trash Problem (資料結構)

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;
}