P5324 [BJOI2019] 删数

感觉思路很明显,但是转化没有很好想。

首先,这个修改 + 查询显然是想让我们搞出易于维护的构造方案,或者把最小修改次数转化成某个东西来维护。

不论怎样,我们先分析一下删除的过程。发现当前长度为 \(i\),那肯定只有 \(i\) 是可以删的。那么不难发现,\(i\) 可以作为删除数的长度范围,其实就是 \([cnt_i - i + 1, i]\)。显然,这些区间的长度之和为 \(n\)。一个序列合法,当且仅当这些区间不重不漏地覆盖了长度 \([1, n]\)

那么如果 \([1, n]\) 中有点没有被覆盖,那么最小修改数一定是没被覆盖的点数,其实这个修改过程相当于把那些重复的,超出范围的,全部改为合法的数。

那么维护非常简单了。另外对于整体加减一的操作,其实就是把查询的 \([1, n]\) 区间平移了。记得判 corner case,如果出现区间右端点 \(> n\) 的情况,那么这个区间不作贡献。

// Problem: P5324 [BJOI2019] 删数
// Contest: Luogu
// Memory Limit: 512 MB
// Time Limit: 1000 ms

#include <bits/stdc++.h>
#define int long long
#define Misaka namespace
#define Network std
using Misaka Network;
const int ZERO = 3e5, N = 1.5e5 + 7;
int n, a[N], q, cnt[ZERO << 1];

struct SGT{
	#define mid ((l + r) >> 1)
	#define ls (x << 1)
	#define rs ((x << 1) | 1)
	int mn[N << 4], cnt[N << 4], lzy[N << 4];
	void pu(int x){
		mn[x] = min(mn[ls], mn[rs]);
		cnt[x] = 0;
		if(mn[x] == mn[ls]) cnt[x] += cnt[ls];
		if(mn[x] == mn[rs]) cnt[x] += cnt[rs];
	}
	void build(int x, int l, int r){
		if(l == r){
			cnt[x] = 1;
			mn[x] = lzy[x] = 0;
			return;
		}
		build(ls, l, mid), build(rs, mid + 1, r);
		pu(x);
	}
	void pd(int x){
		mn[ls] += lzy[x], mn[rs] += lzy[x];
		lzy[ls] += lzy[x], lzy[rs] += lzy[x];
		lzy[x] = 0;
	}
	void modify(int x, int l, int r, int ql, int qr, int k){
		if(ql > qr) return;
		if(ql <= l && r <= qr){
			mn[x] += k, lzy[x] += k;
			return;
		}
		pd(x);
		if(ql <= mid) modify(ls, l, mid, ql, qr, k);
		if(mid < qr)  modify(rs, mid + 1, r, ql, qr, k);
		pu(x);
	}
	int qur(int x, int l, int r, int ql, int qr){
		if(ql > qr) return 0;
		if(ql <= l && r <= qr) return (mn[x] == 0 ? cnt[x] : 0);
		pd(x);
		int res = 0;
		if(ql <= mid) res += qur(ls, l, mid, ql, qr);
		if(mid < qr)  res += qur(rs, mid + 1, r, ql, qr);
		return res;
	}
	void test(int l, int r){
		for(int i = l; i <= r; i ++){
			cout << i << " ";
			cout << 1 - qur(1, 0, (ZERO << 1), ZERO + i, ZERO + i);
			cout << "\n";
		}
	}
} sgt;

signed main(){
	ios::sync_with_stdio(0), cin.tie(0);
	
	cin >> n >> q;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
		cnt[a[i] + ZERO] ++;
	}
	
	sgt.build(1, 0, (ZERO << 1));
	
	
	for(int i = 1; i <= n; i ++){
		if(cnt[i + ZERO]) sgt.modify(1, 0, (ZERO << 1), ZERO + i - cnt[i + ZERO] + 1, ZERO + i, 1);
	}
	
	int l = 1, r = n;
	
	while(q --){
		int opt, x;
		cin >> opt >> x;
		if(opt == 0){
			if(x == 1){
				if(cnt[r + ZERO]) sgt.modify(1, 0, (ZERO << 1), ZERO + r - cnt[r + ZERO] + 1, ZERO + r, -1);
				l --, r --;
			}
			else{
				l ++, r ++;
				if(cnt[r + ZERO]) sgt.modify(1, 0, (ZERO << 1), ZERO + r - cnt[r + ZERO] + 1, ZERO + r, 1);
			}
		}
		else{
			int del = l - 1; x += del;
			if(a[opt] <= r) sgt.modify(1, 0, (ZERO << 1), ZERO + a[opt] - cnt[a[opt] + ZERO] + 1, ZERO + a[opt], -1);
			cnt[a[opt] + ZERO] --;
			if(a[opt] <= r) sgt.modify(1, 0, (ZERO << 1), ZERO + a[opt] - cnt[a[opt] + ZERO] + 1, ZERO + a[opt], 1);
			if(x <= r) sgt.modify(1, 0, (ZERO << 1), ZERO + x - cnt[x + ZERO] + 1, ZERO + x, -1);
			cnt[x + ZERO] ++;
			if(x <= r) sgt.modify(1, 0, (ZERO << 1), ZERO + x - cnt[x + ZERO] + 1, ZERO + x, 1);
			a[opt] = x;
		}
//		sgt.test(l, r);
		cout << sgt.qur(1, 0, (ZERO << 1), ZERO + l, ZERO + r) << "\n";
	}
	
	return 0;
}
posted @ 2026-07-16 13:34  Trent900  阅读(11)  评论(0)    收藏  举报