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;
}
本文来自博客园,作者:Trent900,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/21168162

浙公网安备 33010602011771号