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