对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:
记当前数列长度为kk,则删掉数列中所有等于kk的数。
现有一个长度为nn的数列aa,有mm次修改操作,第ii次修改后你要回答:
经过ii次修改后的数列aa,至少还需要修改几个数才可删空?
每次修改操作为单点修改或数列整体加一或数列整体减一。
输入格式:
第一行两个正整数n,mn,m,分别表示数列长度、修改次数。
第二行有nn个正整数,表示数列aa,即输入的第ii个数表示数列aa的第ii个数a_iai。
接下来mm行,每行两个整数p,xp,x,表示一次修改操作。
当1\le p \le n1≤p≤n时,该操作为单点修改,将数列中第pp个数a_pap修改为xx
当p=0p=0时,该操作为数列整体加xx。
输出格式:
输出mm行,每行一个整数,第ii行表示前ii次修改后的答案。
思路:
简单想一想:
每个数出现的次数用一个桶表示出来。 形成一个个的柱子,然后向左推倒, 答案就是空位的个数。
注意:只有在区间内的点才可以向左推倒。
单点修改 和 区间修改。
具体看洛谷的题解吧。挺详细的。
https://www.luogu.org/problemnew/solution/P5324
#include
#define ls (now << 1)
#define rs (now << 1 | 1)
using namespace std;
const int N = 1e6;
int n,m,op,z,pos,tot;
int cnt[N],a[N],c[N];
int mn[N*4],val[N*4],tag[N*4],sum[N*4];
// mn val tag sum
//区间最小值, 0 的个数, 标记, 最小值的个数,,
// 0 的个数可以由 最小值的个数转移过来。
void pushup(int now){
mn[now] = min(mn[ls],mn[rs]);
sum[now] = (mn[now] == mn[ls]?sum[ls]:0) + (mn[now] == mn[rs]?sum[rs]:0);
val[now] = val[ls] + val[rs];
}
void pushdown(int now){
if (tag[now] == 0) return;
mn[ls] += tag[now];
val[ls] = (mn[ls] == 0 ? sum[ls]: 0);
tag[ls] += tag[now];
mn[rs] += tag[now];
val[rs] = (mn[rs] == 0 ? sum[rs]:0);
tag[rs] += tag[now];
tag[now] = 0;
}
void build(int now, int l, int r){
if (l + 1 == r){
val[now] = sum[now] = 1;
return;
}
int mid = (l + r) / 2;
build(ls,l,mid);
build(rs,mid,r);
pushup(now);
}
void Insert(int now, int l, int r, int a,int b, int k){
if (a <= l && b >= r-1){
mn[now] += k;
val[now] = (mn[now] == 0?sum[now]:0);
tag[now] += k;
return;
}
int mid = (l + r) >> 1;
pushdown(now);
if (a < mid) Insert(ls,l,mid,a,b,k);
if (b >= mid) Insert(rs,mid,r,a,b,k);
pushup(now);
}
int find(int now, int l, int r, int a, int b){
int ans = 0;
if (a <= l && b >= r-1){
return val[now];
}
pushdown(now);
int mid = (l + r) >> 1;
if (a < mid) ans += find(ls,l,mid,a,b);
if (b >= mid) ans += find(rs,mid,r,a,b);
return ans;
}
int main(){
scanf("%d%d",&n,&m);
pos = 2e5+10;
tot = 6e5+10;
for (int i = 1; i <= n; ++i){
scanf("%d",&z);
a[i] = pos + z;
cnt[a[i]]++;
}
build(1,1,tot);
for (int i = pos + 1; i <= pos + n; ++i)
if (cnt[i]) Insert(1,1,tot,i-cnt[i]+1,i,1);
for (int i = 0; i < m; ++i){
scanf("%d%d",&op,&z);
if (op){
int tmp = a[op] - cnt[a[op]] + 1;
cnt[a[op]]--;
if (a[op] <= pos + n)
Insert(1,1,tot,tmp,tmp,-1);
a[op] = pos + z;
cnt[a[op]]++;
tmp = a[op] - cnt[a[op]] + 1;
if (a[op] <= pos + n)
Insert(1,1,tot,tmp,tmp,1);
printf("%d\n",find(1,1,tot,pos+1,pos+n));
} else{
if (z == 1){
Insert(1,1,tot,pos+n-cnt[pos+n]+1,pos + n,-1);
pos--;
} else {
pos++;
Insert(1,1,tot,pos+n-cnt[pos+n]+1,pos+n,1);
}
printf("%d\n",find(1,1,tot,pos+1,pos+n));
}
}
return 0;
}