Description
数轴上有 (n) 个点,每次操作你可以将所有位于 (x) 的点移动到 (x+1) 或者 (x-1)。求将所有点移动到不超过两个整点的最小操作次数。现在对点集做出 (q) 次修改,每次添加一个点,或者删除一个点,你需要动态维护答案。
Solution
假设任意时刻的点的坐标升序排序后的序列为 ({a_i})
考虑到答案一定等于 (max a_i - min a_i - max |a_i - a_{i-1}|),我们只需要动态维护最大的相邻点距离就可以了。
可以维护一个对于所有 (|a_i - a_{i-1}|) 的 multiset
,每次暴力插入删除即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int n,m;
multiset <int> setPos;
multiset <int> setDelta;
void insert(int x)
{
if(setPos.size()==0)
{
setPos.insert(x);
}
else
{
int nSetPosMin=*setPos.begin();
int nSetPosMax=*setPos.rbegin();
if(x<=nSetPosMin)
{
setDelta.insert(nSetPosMin-x);
}
else if(x>=nSetPosMax)
{
setDelta.insert(x-nSetPosMax);
}
else
{
int nPreVal=*(--setPos.lower_bound(x));
int nSucVal=*(setPos.lower_bound(x));
setDelta.erase(setDelta.find(nSucVal-nPreVal));
setDelta.insert(x-nPreVal);
setDelta.insert(nSucVal-x);
}
setPos.insert(x);
}
}
void erase(int x)
{
if(setPos.size()==1)
{
setPos.clear();
}
else
{
auto it=setPos.find(x);
auto itPre=it,itSuc=it;
if(it==setPos.begin())
{
++itSuc;
int nSucVal=*itSuc;
setDelta.erase(setDelta.find(nSucVal-x));
}
else
{
--itPre;
++itSuc;
if(itSuc==setPos.end())
{
int nPreVal=*itPre;
setDelta.erase(setDelta.find(x-nPreVal));
}
else
{
int nPreVal=*itPre;
int nSucVal=*itSuc;
setDelta.insert((nSucVal-nPreVal));
setDelta.erase(setDelta.find(x-nPreVal));
setDelta.erase(setDelta.find(nSucVal-x));
}
}
setPos.erase(setPos.find(x));
}
}
void printans()
{
if(setDelta.size()==0) cout<<0<<endl;
else cout<<*setPos.rbegin()-*setPos.begin()-*setDelta.rbegin()<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
insert(x);
}
printans();
for(int i=1;i<=m;i++)
{
int op,x;
cin>>op>>x;
if(op==0)
{
erase(x);
}
else
{
insert(x);
}
printans();
}
return 0;
}