题意:
有一个长度为
n
n
n的序列,有
m
m
m次操作,有三种操作:第一种是在原序列第
i
i
i个位置的后面插入一个数,如果已经插入过了,那么插在第
i
i
i个位置之前插入的最后一个的后面,新插入的后一个为原序列第
i
+
1
i+1
i+1个数。第二种操作是询问已有数字相邻的两两之间差绝对值的最小值。第三种操作是询问已有数字中任意两数之差绝对值的最小值。
n
,
m
<
=
5
e
5
n,m<=5e5
n,m<=5e5
题解:
本来想找点平衡树的题目的,于是找到了这个题。但是发现可以直接STL做了。
使用两个multiset和一个堆来维护。插入时不断更新每个原位置插入的最后一个数是多少,用个数组记录就可以了。
第一个multiset记录已有的所有值,配合堆来维护第三种操作。堆里记录目前所有可能绝对值最小的差值。每次插入一个数,就在第一个multiset里lower_bound找前驱和后继,用这两个和当前数做差加入堆里。
第二个multiset维护已有的所有相邻差值。每当新插入一个,就把这个位置原有的和后面一个数的差值删去,再加上那两个数和当前数的差值。
这个题就做完了,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,a[500010],lst[500010];
priority_queue<int> q;
multiset<int> s1,s2;
char s[100];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
lst[i]=a[i];
if(i!=1)
{
multiset<int>::iterator it;
it=s1.lower_bound(a[i]);
if(it!=s1.begin())
{
multiset<int>::iterator it2=it--;
q.push(-abs(a[i]-(*it2)));
}
q.push(-abs((*it)-a[i]));
}
s1.insert(a[i]);
if(i>=2)
s2.insert(abs(a[i]-a[i-1]));
}
for(int qq=1;qq<=m;++qq)
{
scanf("%s",s+1);
if(s[1]=='I')
{
int x,y;
scanf("%d%d",&x,&y);
if(x!=n)
{
s2.erase(s2.find(abs(a[x+1]-lst[x])));
s2.insert(abs(a[x+1]-y));
}
s2.insert(abs(lst[x]-y));
multiset<int>::iterator it;
it=s1.lower_bound(y);
if(it!=s1.begin())
{
multiset<int>::iterator it2=it--;
q.push(-abs(y-(*it2)));
}
q.push(-abs((*it)-y));
lst[x]=y;
s1.insert(y);
}
else if(s[5]=='S')
printf("%d\n",-q.top());
else
printf("%d\n",*s2.begin());
}
return 0;
}