Description
Solution
首先肯定是考虑在没有修改的情况下,直接给定序列该如何计算该序列需要修改几个数才能删空:
首先,该序列的顺序对答案是没有任何影响的,影响答案的只有某个数的出现次数,这令我们想到在值域数轴上用桶维护每个数的出现次数。
接下来考虑模拟删边的过程,一开始长度为 \(n\),删去所有数列中等于 \(n\) 的数,这等价于将 \(n\) 对应的桶向左推倒,将所有 \(n\) 依次覆盖在数轴上,对所有数的进行这样的操作,有结论:数轴上 \([1,n]\) 区域内未被覆盖的位置的数量就是答案。
证明:
设未被覆盖的位置的数量为 \(w\),首先答案数量必定会 \(\ge w\) ,这是因为数轴上这 \(w\) 个空缺位置必须要被填补,才能使得后面的数被删完后,可以开始删前面的数。其次,由于空缺了 \(w\) 个位置,那么数轴上一定有 \(w\) 个位置被重复覆盖,一定可以取出这 \(w\) 个数将它们放到空位上,因此答案 \(\le w\),所有答案 \(=w\)。
回到原问题,考虑直接维护数轴上每个数被覆盖的情况,那么询问就等价于询问区间中 \(0\) 的个数,这可以通过维护最小值+最小值的出现次数实现。对于修改,单点修改只会改变一个位置的覆盖情况,直接修改即可;整体 \(+1\) 或 \(-1\) 则等价于是让询问区间在数轴上平移,可以通过将数轴向左右扩展 \(150000\) 格,一起维护来实现。同时此时我们只关心询问区间 \([l+1,l+n]\) 区间内的数的贡献,因此每次单点修改都需要考虑其在询问区间内才进行修改,每次询问区间平移一个,都需要更改左右两个位置的桶的贡献,这等价于是一个区间修改,也是易于维护的。
总复杂度 \(\mathcal O(n\log n)\)。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+10,lim=600000,len=200000;
int n,m,a[N],ct[N],buc[N];
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid ((l+r)>>1)
int tr[N<<2],tag[N<<2],mn[N<<2],ct[N<<2];
inline void pushdown(int p){
if(!tag[p]) return ;
tag[lc]+=tag[p];mn[lc]+=tag[p];
tag[rc]+=tag[p];mn[rc]+=tag[p];
tag[p]=0;
}
inline void pushup(int p){
mn[p]=min(mn[lc],mn[rc]);ct[p]=0;
if(mn[p]==mn[lc]) ct[p]+=ct[lc];
if(mn[p]==mn[rc]) ct[p]+=ct[rc];
}
inline void build(int p=1,int l=1,int r=lim){
tag[p]=0;
if(l==r){mn[p]=buc[l];ct[p]=1;return ;}
build(lc,l,mid);build(rc,mid+1,r);
pushup(p);
}
inline void update(int p,int ql,int qr,int v,int l=1,int r=lim){
if(ql<=l&&r<=qr){
tag[p]+=v;mn[p]+=v;
return ;
}
pushdown(p);
if(ql<=mid) update(lc,ql,qr,v,l,mid);
if(qr>mid) update(rc,ql,qr,v,mid+1,r);
pushup(p);
}
inline int query(int p,int ql,int qr,int l=1,int r=lim){
if(ql<=l&&r<=qr)
return mn[p]==0?ct[p]:0;
pushdown(p);
int ans=0;
if(ql<=mid) ans+=query(lc,ql,qr,l,mid);
if(qr>mid) ans+=query(rc,ql,qr,mid+1,r);
return ans;
}
}
int del,s;
inline void dele(int x){SGT::update(1,x-ct[x]+1,x,-1);}
inline void add(int x){SGT::update(1,x-ct[x]+1,x,1);}
int main(){
scanf("%d%d",&n,&m);
del=200000;s=del;
for(int i=1;i<=n;++i) scanf("%d",&a[i]),a[i]+=del,ct[a[i]]++;
for(int i=0;i<=n;++i){
int x=i+del;
for(int j=x-ct[x]+1;j<=x;++j) buc[j]++;
}
SGT::build();
for(int i=1,op,x;i<=m;++i){
scanf("%d%d",&op,&x);
if(op>=1){
x+=s;
if(a[op]!=x){
if(a[op]<=n+s&&a[op]>s) SGT::update(1,a[op]-ct[a[op]]+1,a[op]-ct[a[op]]+1,-1);
ct[a[op]]--;
a[op]=x;
ct[x]++;
if(x<=n+s&&x>s) SGT::update(1,x-ct[x]+1,x-ct[x]+1,1);
}
}
else{
if(x==-1)
dele(s+1),add(s+n+1),s++;
else
dele(s+n),add(s),s--;
}
printf("%d\n",SGT::query(1,s+1,s+n));
}
return 0;
}