传送门
对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:
现有一个长度为 n n n 的数列 a a a,有 m m m 次修改操作,第 i i i 次修改后你要回答:经过 i i i 次修改后的数列 a a a,至少还需要修改几个数才可删空?
每次修改操作为单点修改或数列整体加一或数列整体减一。
数据范围: 1 ≤ n , m ≤ 150000 1≤n,m≤150000 1≤n,m≤150000。
这道题的想法很妙啊。
首先,我们只需考虑每个数 i i i 的出现次数 c n t i cnt_i cnti,并不关心数的排列顺序。
考虑一个结论,对于 i i i,我们把 [ i − c n t i + 1 , i ] [i-cnt_i+1,i] [i−cnti+1,i] 这个区间进行一个区间覆盖,答案就是没有被覆盖的数的个数。
怎么理解这个东西呢?这有点像贪心。首先,对于 [ 1 , n ] [1,n] [1,n] 的数是有可能不用修改的,考虑怎么最大化不修改的数的数量。如果对于 i i i,把 [ i − c n t i + 1 , i ] [i-cnt_i+1,i] [i−cnti+1,i] 覆盖,就相当于这一段可以不改。重复覆盖就相当于 “ “ “冲突 ” ” ”,就会空出几个位置。
修改的话,单点修改比较好做,整体加减 1 1 1 就把区间整体移一下就好了。
然后注意一下边界的问题就可以了。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include
#define N 1000005
using namespace std;
int n,m,S,lim,a[N];
namespace Segment_Tree{
struct Seg{int add,zero,Min,cnt;}T[N<<2];
#define mid ((l+r)>>1)
void pushup(int root){
T[root].Min=min(T[root<<1].Min,T[root<<1|1].Min);
T[root].cnt=(T[root].Min==T[root<<1].Min?T[root<<1].cnt:0)+(T[root].Min==T[root<<1|1].Min?T[root<<1|1].cnt:0);
T[root].zero=T[root<<1].zero+T[root<<1|1].zero;
}
void pushnow(int root,int val){
T[root].Min+=val;
T[root].add+=val;
T[root].zero=(T[root].Min==0)?T[root].cnt:0;
}
void pushdown(int root){
if(!T[root].add) return;
pushnow(root<<1,T[root].add);
pushnow(root<<1|1,T[root].add);
T[root].add=0;
}
void build(int root,int l,int r){
if(l==r){T[root].zero=T[root].cnt=1;return;}
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
pushup(root);
}
void Modify(int root,int l,int r,int x,int y,int val){
if(l>=x&&r<=y) {pushnow(root,val);return;}
pushdown(root);
if(x<=mid) Modify(root<<1,l,mid,x,y,val);
if(y> mid) Modify(root<<1|1,mid+1,r,x,y,val);
pushup(root);
}
int Query(int root,int l,int r,int x,int y){
if(l>=x&&r<=y) return T[root].zero;
pushdown(root);
if(y<=mid) return Query(root<<1,l,mid,x,y);
if(x> mid) return Query(root<<1|1,mid+1,r,x,y);
return Query(root<<1,l,mid,x,y)+Query(root<<1|1,mid+1,r,x,y);
}
#undef mid
}
using namespace Segment_Tree;
int num[N];
void change(int x,int val){
int k=num[S+x]+(val>0);
num[S+x]+=val;
if(x<=n) Modify(1,1,lim,S+x-k+1,S+x-k+1,val);
}
int main(){
scanf("%d%d",&n,&m),S=n+m+1,lim=S*2+1,build(1,1,lim);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),change(a[i],1);
int p,x,delta=0;
while(m--){
scanf("%d%d",&p,&x);
if(p>0) change(a[p]+delta,-1),a[p]=x-delta,change(a[p]+delta,1);
else if(x==1){
int pos=S+n;S--,delta++;
if(num[pos]>0) Modify(1,1,lim,pos-num[pos]+1,pos,-1);
}
else{
int pos=S+n+1;S++,delta--;
if(num[pos]>0) Modify(1,1,lim,pos-num[pos]+1,pos,1);
}
printf("%d\n",Query(1,1,lim,S+1,S+n));
}
return 0;
}