首页 > 其他分享 >P5324 [BJOI2019] 删数

P5324 [BJOI2019] 删数

时间:2024-03-24 20:44:55浏览次数:24  
标签:BJOI2019 P5324 覆盖 int st 删数 buc define

P5324 [BJOI2019] 删数

转化条件+线段树

由于值域不大,并且删数操作跟序列顺序无关,只和每个数的出现次数有关,考虑在值域上分析删数操作。发现对于每一个值 \(i\) 可以抽象为覆盖了 \([i-buc_{i}+1,i]\) 的区间。要使数列删空,就要让 \([1,n]\) 被填满。这样我们就会发现答案就是 \([1,n]\) 中未被覆盖的值的数量。

证明:

  1. 首先对于每个空,我们至少要移动一次使得空被填满。所以空缺的数量是答案下界。
  2. 假如空的数量为 \(k\),那么一定有 \(k\) 个被多次覆盖或不在 \([1,n]\) 范围内的值,使得将这些位置的值修改为这些空的值后数列能够被删空。

接下来考虑修改操作,

对于单点修改,实际上就是删去原来的覆盖,添加新的覆盖。

对于数列整体加,可以看成覆盖位置的平移,等价于判断范围(指 \([1,n]\))的相反移动。所以维护判断范围的起始位置 \(st\),那么只需要查询 \([st+1,st+n]\) 未被覆盖的值的数量即可。

于是考虑用线段树维护这些操作。维护区间最小值 \(mn\)、最小值数量 \(mncnt\)、答案 \(ans\) 以及懒标记。

关于合并,其他显然。当 \(mn=0\) 时,\(mncnt\) 即为 \(ans\)

一些细节:

显然在 \([st+1,st+n]\) 之外的值无法贡献覆盖,所以需要在判断范围移动的时候撤销或添加右端点的覆盖。

\(st\) 可以为负数,所以需要将线段树加起始值防止越界。

复杂度 \(O(n\log n)\)。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

typedef long long i64;
const int N = 150010;
int n, m, st, lim;
int a[N], buc[3 * N];
struct seg {
	int mn;
	int mncnt, ans;
	int lzy;
} t[(3 * N) << 2];
void pushup(int u) {
	t[u].ans = t[u << 1].ans + t[u << 1 | 1].ans;
	t[u].mn = std::min(t[u << 1].mn, t[u << 1 | 1].mn);
	t[u].mncnt = 0;
	if(t[u].mn == t[u << 1].mn) t[u].mncnt += t[u << 1].mncnt;
	if(t[u].mn == t[u << 1 | 1].mn) t[u].mncnt += t[u << 1 | 1].mncnt;
}
void build(int u, int l, int r) {
	if(l == r) {
		t[u] = {0, 1, 1, 0};
		return;
	}
	int mid = (l + r) >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
	pushup(u);
}
void pushdown(int u) {
	if(!t[u].lzy) return;
	t[u << 1].mn += t[u].lzy, t[u << 1 | 1].mn += t[u].lzy;
	t[u << 1].lzy += t[u].lzy, t[u << 1 | 1].lzy += t[u].lzy;
	if(!t[u << 1].mn) t[u << 1].ans = t[u << 1].mncnt;
	else t[u << 1].ans = 0;
	if(!t[u << 1 | 1].mn) t[u << 1 | 1].ans = t[u << 1 | 1].mncnt;
	else t[u << 1 | 1].ans = 0;
	t[u].lzy = 0;
}
void update(int u, int l, int r, int L, int R, int x) {
	if(L <= l && r <= R) {
		t[u].mn += x;
		t[u].lzy += x;
		if(!t[u].mn) t[u].ans = t[u].mncnt;
		else t[u].ans = 0;
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(u);
	if(L <= mid) update(u << 1, l, mid, L, R, x);
	if(R > mid) update(u << 1 | 1, mid + 1, r, L, R, x);
	pushup(u);
}
int query(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		return t[u].ans;
	}
	int mid = (l + r) >> 1, ret = 0;
	pushdown(u);
	if(L <= mid) ret += query(u << 1, l, mid, L, R);
	if(R > mid) ret += query(u << 1 | 1, mid + 1, r, L, R);
	return ret;
}

void Solve() {
	std::cin >> n >> m;

	st = N - 10, lim = 3 * N - 30;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i];
		a[i] += st;
		buc[a[i]]++;
	}
	build(1, 1, lim);
	for(int i = st + 1; i <= st + n; i++) {
		if(buc[i]) update(1, 1, lim, i - buc[i] + 1, i, 1);
	}
	while(m--) {
		int p, x;
		std::cin >> p >> x;
		if(p) {
			if(a[p] <= st + n) {
				update(1, 1, lim, a[p] - buc[a[p]] + 1, a[p] - buc[a[p]] + 1, -1);
			}
			buc[a[p]]--;
			a[p] = st + x;
			if(a[p] <= st + n) {
				update(1, 1, lim, a[p] - buc[a[p]], a[p] - buc[a[p]], 1);
			}
			buc[a[p]]++;
		} else {
			if(x == 1) {
				if(buc[st + n]) {
					update(1, 1, lim, st + n - buc[st + n] + 1, st + n, -1);
				}
				st--;
			}
			else {
				st++;
				if(buc[st + n]) {
					update(1, 1, lim, st + n - buc[st + n] + 1, st + n, 1);
				}
			}
		}
		std::cout << query(1, 1, lim, st + 1, st + n) << "\n";
	}
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}

标签:BJOI2019,P5324,覆盖,int,st,删数,buc,define
From: https://www.cnblogs.com/FireRaku/p/18092991

相关文章

  • P5322 [BJOI2019] 排兵布阵
    原题链接题解巧妙的背包问题我可以用按顺序遍历城堡,顺便表示出遍历到当前城堡时用掉了多少兵力,这样是可以穷尽所有兵力派送情况的同时把这个城堡里的敌方兵力升序排序,然后遍历,表示为了消灭所有兵力小于等于ta的敌人所加的分code#include<bits/stdc++.h>usingnamespacestd......
  • 洛谷题单指南-贪心-P1106 删数问题
    原题链接:https://www.luogu.com.cn/problem/P1106题意解读:如何删数,让剩下的数最小,贪心选择问题。解题思路:先看样例:1754384第1次遍历:删掉7,剩下15438第2次遍历:删掉5,剩下1438第3次遍历:删掉4,剩下138第4次遍历:删掉8,剩下13,即为结果所以,贪心策略如下:1、遍历每一个数,如果前一......
  • EAS_查询分析器中误删数据恢复
    1、通过flashbackquery查询某历史时点的数据量,找到删除时间点的前1s,如2020-05-1316:38:07秒钟误删的数据:selectcount(*)fromt_testasoftimestampto_timestamp('2020-05-1316:38:06','yyyy-mm-ddhh24:mi:ss');--查出5000条数据,查询6s时的数据select*fro......
  • P5321 [BJOI2019] 送别 题解--zhengjun
    由于大家的做法需要大量分类讨论和代码量,这里提供一种不怎么分类的,容易实现的做法。首先,由于墙体会随时变化,所以直接对墙体本身维护不是很方便。我们可以牺牲一点常数,对\((i,j)\)建立四个点\(UL_{i,j},UR_{i,j},DL_{i,j},DR_{i,j}\)分别表示\((i-\varepsilon,j-\varepsilo......
  • 通过备份 Etcd 来完美恢复 Kubernetes 中的误删数据
    误删除或者服务器宕机,会导致Etcd数据的丢失或某个节点的Etcd数据异常时,当误删时,需要恢复数据,这个在实际环境当中是不可避免的。以下描述删除两个namespace下的Pod,如何恢复对应namespace的数据。1、操作环境3个(master、etcd)+1个node新建1个namespace下且创建Pod和......
  • 数据打标前的处理,删数字和没用的特殊符号
    importosimportredefclean_filename(filename):#保留括号内的数字filename=re.sub(r'(?<!\()\d+(?!\))','',filename)#将特殊符号(包括下划线)转换为空格,但保留括号、逗号和句点filename=re.sub(r'[^\w\s\(\),\.]|_',''......
  • P5323 [BJOI2019] 光线
    P5323[BJOI2019]光线题目描述当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收。设对于任意\(x\),有\(x\timesa_i\%\)单位的光会穿过它,有\(x\timesb_i\%\)的会被反射回去。现在\(n\)层玻璃叠在一起,有\(1\)单位......
  • oracle-使用delete误删数据后的恢复方法
    今天误删数据,学习到了使用闪回恢复数据的方法通过闪回日志可以使数据库恢复到过去的某个状态--查看对应时间点对应表的数据select*from'table_name'asoftimestampto_timestamp('2023-11-0407:00:00','yyyy-mm-ddhh24:mi:ss')--如果被禁用行移动altertable'table......
  • mysql误删数据恢复
    1,是否有备份,可以从备份里边恢复,2、通过工具从数据库binlog日志恢复(前提开始binlog日志功能)。使用my2sql工具进行恢复官网地址:https://github.com/liuhr/my2sql按照官网操作编译,或者直接下载编译好的工具如图所示 将下载好的my2sql的工具 上传到/usr/local/bin目录下 赋......
  • P1106 删数问题
    P1106删数问题对2018年的我一次完美的对位单杀201844ptsCode#include<cstdio>#include<iostream>#include<algorithm>usingstd::cin;usingstd::cout;usingstd::sort;usingstd::pair;usingstd::string;stringzsk_lu_ping;structzsksb{intplace;......