题意:
对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:
- 记当前数列长度为 k,则删掉数列中所有等于 k 的数。
现有一个长度为 n 的数列 a,有 m 次修改操作,第 i 次修改后你要回答:经过 i 次修改后的数列 a,至少还需要修改几个数才可删空?
每次修改操作为单点修改或数列整体加一或数列整体减一。
题解:
如果一个我要删去大小为a的数,那么序列长度会变成a-h[a](h[a]为数值a出线的次数)。那么我们意会一下这个情况,我们可以想象成有h[a]个箱子堆在了a上面,然后向左倾倒,一个一个地落在位置上。当然,大于序列长度的位置的箱子不能考虑进来。
那么答案是什么?答案就是没有箱子的位置数量。因为我可以把堆了多个箱子的位置上的箱子移到没有箱子的地方,移动次数也就是答案。
那么如果只有单点修改的话,我们用一个线段树维护一下就好了。
然而有数列整体加减1怎么办呢。没有关系,我们只需要当成数字没变,询问的区间位移了就可以了。当然,后加入的数字要跟着位移。
于是我们又可以使用线段树维护了!
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; const int INF=300000; int n,m,a[300002],h[900002],t,cnt=1,minn,ans; typedef struct{ int ls,rs,Min,sum,f; }P; P p[4000002]; void pushdown(int root){ if (p[root].f) { if (p[root].ls) { p[p[root].ls].Min+=p[root].f;p[p[root].ls].f+=p[root].f; } if (p[root].rs) { p[p[root].rs].Min+=p[root].f;p[p[root].rs].f+=p[root].f; } p[root].f=0; } } void build(int root,int begin,int end){ if (begin==end) { p[root].Min=0;p[root].sum=1; return; } int mid=begin+(end-begin)/2; p[root].ls=++cnt;p[root].rs=++cnt; build(p[root].ls,begin,mid);build(p[root].rs,mid+1,end); int ls=p[root].ls,rs=p[root].rs; if (p[ls].Min<p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum;} else if (p[ls].Min>p[rs].Min){p[root].Min=p[rs].Min;p[root].sum=p[rs].sum;} else if (p[ls].Min==p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum+p[rs].sum;} } void gengxin(int root,int begin,int end,int begin2,int end2,int x){ if (begin>end2 || end<begin2)return; if (begin>=begin2 && end<=end2) { p[root].Min+=x;p[root].f+=x; return; } int mid=begin+(end-begin)/2;pushdown(root); gengxin(p[root].ls,begin,mid,begin2,end2,x);gengxin(p[root].rs,mid+1,end,begin2,end2,x); int ls=p[root].ls,rs=p[root].rs; if (p[ls].Min<p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum;} else if (p[ls].Min>p[rs].Min){p[root].Min=p[rs].Min;p[root].sum=p[rs].sum;} else if (p[ls].Min==p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum+p[rs].sum;} } void chaxun(int root,int begin,int end,int begin2,int end2){ if (begin>end2 || end<begin2)return; if (begin>=begin2 && end<=end2) { if (p[root].Min<minn) { minn=p[root].Min;ans=p[root].sum; } else if (p[root].Min==minn)ans+=p[root].sum; return; } int mid=begin+(end-begin)/2;pushdown(root); chaxun(p[root].ls,begin,mid,begin2,end2);chaxun(p[root].rs,mid+1,end,begin2,end2); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){scanf("%d",&a[i]);h[a[i]+INF]++;} t=n;build(1,-INF,INF); for (int i=1;i<=n;i++) if (h[i+INF])gengxin(1,-INF,INF,i-h[i+INF]+1,i,1); for (int i=1;i<=m;i++) { int op,x; scanf("%d%d",&op,&x); if (op) { if (h[a[op]+INF] && a[op]<=t)gengxin(1,-INF,INF,a[op]-h[a[op]+INF]+1,a[op]-h[a[op]+INF]+1,-1); h[a[op]+INF]--;x=t-n+x;h[x+INF]++; if (h[x+INF])gengxin(1,-INF,INF,x-h[x+INF]+1,x-h[x+INF]+1,1); a[op]=x; } else { if (x==-1) { t++; if (h[t+INF])gengxin(1,-INF,INF,t-h[t+INF]+1,t,1); } else { if (h[t+INF])gengxin(1,-INF,INF,t-h[t+INF]+1,t,-1); t--; } } minn=INF;ans=0; chaxun(1,-INF,INF,t-n+1,t); if (minn)puts("0");else printf("%d ",ans); } return 0; }