*题解:P5324 [BJOI2019] 删数
解析
考虑初始数列的答案怎么求。显然,数字顺序不影响答案,故开一个桶 \(b\) 把所有数字的出现次数存进来。根据题意,一个序列可以删空当且仅当如果令每种数 \(b_i\) 覆盖其左边的区间 \([i - b_i + 1,i]\),那么最终每个位置都能被覆盖到。对于没有被覆盖到的位置,必定可以通过一次操作修改被重复覆盖以及超出边界的点,从而将其覆盖。而且,通过一次操作至多能更改两个位置的状态,且其中一个位置必定未被覆盖。故这种操作方式是最优的,答案为没有覆盖到的位置数量。
对于只有单点修改的情况,直接维护一个覆盖的数组 \(c\) 表示每个位置被覆盖的次数即可。修改必定是某个位置减一某个位置加一,维护数组中 \(0\) 的个数是容易的。
现在有了数列整体减,对于 \(c\) 而言,相当于里面的元素整体向左平移一个单位,这个东西不好维护,所以不妨换个角度考虑,把查询区间向右移动一个单位即可。设当前查询区间为 \([L,R]\),那么只需要移出 \(L\),加入 \(R + 1\) 即可,同样可以直接维护。
对于数列整体加,类似地,把查询区间向左移动一个单位。然而,对于查询区间外的数,可能会对区间内做多余的贡献,设当前查询区间为 \([L,R]\),每左移一个单位,就要减去 \(b_R\) 所做的覆盖。这一步骤需要区间减。同时这也使得数列整体加操作要加上 \(b_{R + 1}\) 所作的覆盖。
对于单点修改操作,修改的值需要根据当前询问区间加上一个偏移量 \(x\),怎么判断是加还是减呢?为了方便查询我们将询问区间视为可变的,但实际上它是不动的,我们想要添加一个值也是在这个不动的区间里添加,所以添加的元素相对于区间的位置是固定的。例如当整体加一时,区间向左移动,后面添加的元素也视为向左移动,故 \(x \leftarrow x-1\)。
因此,我们需要维护数列区间加,区间 \(0\) 的个数。对于区间 \(0\) 的个数,由于 \(c\) 中元素非负,所以我们只关心区间最小值是否为 \(0\),如果是,其个数为多少。线段树维护即可。
时间复杂度 \(O(m \log n)\)。
代码
注意由于整体加减的存在,需要开三倍空间。
/*
*/
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)
#define mid (l + r >> 1)
using namespace std;
const int N = 150000 + 5,M = 3 * N;
typedef long long ll;
typedef pair<int,int> pii;
int a[N],b[M];
int mn[M << 2],cnt[M << 2],tag[M << 2];
void push_up(int p){
mn[p] = min(mn[ls(p)],mn[rs(p)]);
cnt[p] = 0;
if(mn[ls(p)] <= mn[rs(p)]) cnt[p] += cnt[ls(p)];
if(mn[ls(p)] >= mn[rs(p)]) cnt[p] += cnt[rs(p)];
}
void build(int p,int l,int r){
if(l == r){
cnt[p] = 1;
return;
}
build(ls(p),l,mid),build(rs(p),mid + 1,r);
push_up(p);
}
void add_tag(int p,int k){
tag[p] += k;
mn[p] += k;
}
void push_down(int p){
if(!tag[p]) return;
add_tag(ls(p),tag[p]);
add_tag(rs(p),tag[p]);
tag[p] = 0;
}
void modi(int p,int l,int r,int k,int x){
if(l == r){
mn[p] = x;
return;
}
push_down(p);
modi(ls(p),l,mid,k,x),modi(rs(p),mid + 1,r,k,x);
push_up(p);
}
int ask(int p,int l,int r,int L,int R){
if(l > R || r < L) return 0 ;
if(l >= L && r <= R){
return (mn[p] == 0) * cnt[p];
}
push_down(p);
return ask(ls(p),l,mid,L,R) + ask(rs(p),mid + 1,r,L,R);
}
void add(int p,int l,int r,int L,int R,int x){
if(l > R || r < L) return;
if(l >= L && r <= R){
add_tag(p,x);
return;
}
push_down(p);
add(ls(p),l,mid,L,R,x),add(rs(p),mid + 1,r,L,R,x);
push_up(p);
}
int val(int p,int l,int r,int k){
if(l > k || r < k) return 2e9;
if(l == r){
return mn[p];
}
push_down(p);
return min(val(ls(p),l,mid,k),val(rs(p),mid + 1,r,k));
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i] += N - 1;
b[a[i]]++;
}
int l = N,r = l + n - 1;
build(1,1,M);
for(int i=l;i<=r;i++){
add(1,1,M,i - b[i] + 1,i,1);
}
int c = 0;
while(m--){
int p,x;
cin>>p>>x;
if(p == 0){
if(x == 1){
add(1,1,M,r - b[r] + 1,r,-1);
c--;
l--,r--;
}else{
c++;
l++,r++;
add(1,1,M,r - b[r] + 1,r,1);
}
cout<<ask(1,1,M,l,r)<<'\n';
}else{
x += N - 1 + c;
if(a[p] <= r)
add(1,1,M,a[p] - b[a[p]] + 1,a[p] - b[a[p]] + 1,-1);
b[a[p]]--;
if(x <= r)
add(1,1,M,x - b[x],x - b[x],1);
b[x]++;
a[p] = x;
cout<<ask(1,1,M,l,r)<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号