如何删除给定索引之间的所有元素
How to remove all element between given indexes?
简要介绍问题的条件:
给定从1到n的数字,以及m个纯化阶段,之后在m行中,左右两个数字(包括边界),删除数字的范围(1..n),您必须输出删除后的所有生命元素。
我将举一个例子:
n = 10, m = 3
假设我们制作一个数组a[1,2,3,4,5,6,7,8,9,10];
left = 1, right = 2;
删除1个后:a[3,4,5,6,7,8,9,10];
left = 4, right = 5;
删除2个后:a[3,4,5,8,9,10];
left = 3, right = 5;
删除3个后:a[3,4,10];
Conclusion: 3 4 10
所以并不是每件事都那么简单,限制是严格的,即:
n, m <= 3 * 10 ^ 5
left <= right
我的尝试如下:我创建了一个从1到n的数字向量,并删除了范围[左,右]中的所有元素,但由于复杂性,时间限制即将到来。
#include <iostream>
#include <vector>
using namespace std;
#define ll uint64_t
int main() {
ll i, n, k, l, r;
cin >> n >> k;
vector <ll> a;
for (i = 1; i <= n; i++) {
a.push_back(i);
}
for (i = 1; i <= k; i++) {
cin >> l >> r;
a.erase(a.begin()+l-1,a.begin()+r);
}
cout << a.size() << endl;
for (auto i : a) {
cout << i << ' ';
}
}
如何解决这个问题?
使用具有延迟传播和O((N+Q)*log(N))中的顺序统计的分段树可以解决该问题,考虑到您的限制,该分段树应该在一两秒钟内通过大多数在线评委。
简要说明
"仍然存在">布尔数组
让我们想象一下,我们有一个大小N的布尔数组,它指示每个项目是否仍然存在或已删除。由于还没有删除任何元素,数组将用1初始化。
分段树
信息查询:让我们在这个布尔数组的顶部构建一个范围和段树(True映射到1,false映射到0)。如果我们查询任何[L,R]范围,则分段树的答案是仍然存在的元素的数量(注意,L和R是原始数组中的索引,包括已移除和未移除的元素)
更新查询:对段树执行的唯一更新查询是用零设置一个范围(将元素范围标记为已删除)。由于我们将一系列元素更新为零,因此需要使用延迟传播(如果问题需要删除单个项,则不需要延迟传播)。
最终输出
在更新所有为零的范围后,我们可以迭代每个索引,检查它是零还是一,如果是一,则打印它。然而,解决方案并不容易,因为输入中提供的范围不是原始数组中的范围,而是更新数组中的索引。
更新的范围问题
为了更好地理解这个问题,让我们来看看一个例子:
假设我们使用的是一个长度为6的数组,数组最初为:1 2 3 4 5 6,布尔数组最初为
让我们假设第一个删除是[2,4],现在新的数组是:1 5 6,新更新的布尔数组是:1 0 0 1 1
在这一点上,如果我们被要求打印数组,我们将简单地遍历原始数组,并打印布尔数组中只对应于true的值。
现在,让我们尝试删除另一个范围[1,2],如果我们简单地将前两个元素设置为零,那么我们将以:0 0 0 1 1结束。这意味着我们的数组中仍然有5,6,而在最后一次删除后,我们实际上只有6 为了解决这个问题,我们需要将order statistics属性添加到我们的分段树中。这个属性将回答以下问题:给定X,找到索引是以它结尾的前缀和X。这将帮助我们将当前[L,R]映射到可以与原始索引一起使用的新[L,R]。 为了更好地理解映射,让我们回到示例的第二步: 布尔数组是:1 0 0 1 1,删除L=1和R=2之间的元素,使用order statistics属性,L将映射到1,R将映射到5,现在我们将更新newL和newR之间的范围为零,布尔数组变为0 0 0 0 1。 如果你不熟悉分段树,那么它可能会发现代码很难理解,所以我会忽略分段树的内部实现,让它变得更容易,并让你快速了解它的功能。 query方法将要查询的范围的起始索引和结束索引作为输入,并返回该范围内元素的总和。 更新方法将要更新的范围的开始和结束索引作为输入,并将该范围内的所有项目设置为零 find方法将X作为输入,并返回第一个元素Y是[0,Y]范围内元素的总和X 该问题也可以使用Splay Tree或Treap数据结构来解决。订单统计以解决更新范围问题
代码
#include <bits/stdc++.h>
using namespace std;
class SegmentTree {
vector<int> seg, lazy;
int sz;
void build(int ind, int ns, int ne, const vector<int> &v) {
if (ns == ne) {
seg[ind] = v[ns];
return;
}
int mid = ns + (ne - ns) / 2;
build(ind * 2, ns, mid, v);
build(ind * 2 + 1, mid + 1, ne, v);
seg[ind] = seg[ind * 2] + seg[ind * 2 + 1];
}
void probagateLazy(int ind) {
if (lazy[ind]) {
lazy[ind] = 0, seg[ind] = 0;
if (ind * 2 + 1 < 4 * sz)
lazy[ind * 2] = lazy[ind * 2 + 1] = 1;
}
}
int query(int s, int e, int ind, int ns, int ne) {
probagateLazy(ind);
if (e < ns || s > ne)
return 0;
if (s <= ns && ne <= e)
return seg[ind];
int mid = ns + (ne - ns) / 2;
return query(s, e, ind * 2, ns, mid) + query(s, e, ind * 2 + 1, mid + 1, ne);
}
void update(int s, int e, int ind, int ns, int ne) {
probagateLazy(ind);
if (e < ns || s > ne)
return;
if (s <= ns && ne <= e) {
lazy[ind] = 1;
probagateLazy(ind);
return;
}
int mid = ns + (ne - ns) / 2;
update(s, e, ind * 2, ns, mid);
update(s, e, ind * 2 + 1, mid + 1, ne);
seg[ind] = seg[ind * 2] + seg[ind * 2 + 1];
}
int find(int pos, int ind, int ns, int ne) {
probagateLazy(ind);
if (ns == ne)
return ns;
probagateLazy(ind * 2);
probagateLazy(ind * 2 + 1);
int mid = ns + (ne - ns) / 2;
if (pos <= seg[ind * 2])
return find(pos, ind * 2, ns, mid);
return find(pos - seg[ind * 2], ind * 2 + 1, mid + 1, ne);
}
public:
SegmentTree(int sz, const vector<int> &v) {
this->sz = sz;
seg = vector<int>(sz * 4);
lazy = vector<int>(sz * 4);
build(1, 0, sz - 1, v);
}
int query(int s, int e) {
return query(s, e, 1, 0, sz - 1);
}
int update(int s, int e) {
update(s, e, 1, 0, sz - 1);
}
int find(int pos) {
return find(pos, 1, 0, sz - 1);
}
};
int main() {
int i, n, k, l, r;
scanf("%d %d", &n, &k);
vector<int> a;
for (i = 1; i <= n; i++) {
a.push_back(i);
}
vector<int> v(n, 1);
SegmentTree st(n, v);
while (k--) {
scanf("%d %d", &l, &r);
int newL = st.find(l);
int newR = st.find(r);
st.update(newL, newR);
}
vector<int> ans;
for (int i = 0; i < n; i++) {
if (st.query(i, i))
ans.push_back(a[i]);
}
printf("%dn", ans.size());
for (int i = 0; i < ans.size(); i++) {
printf("%d ", ans[i]);
}
}
黑拳段树
查询方法
更新方法
查找方法
其他解决方案
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 数组索引的值没有增加
- int(c) 和 c-'0' 之间的区别。C++
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 在cuda线程之间共享大量常量数据
- 查找字符在两个索引之间出现的次数
- C++ 复制字符串的指定索引之间的任何子字符串
- 查找C++中两个索引之间的子字符串
- 在成员函数之间传递const变量为数组的索引
- 是否有C 功能来计算两个索引之间的距离
- 从 1 中创建新的字符 * 作为参数,元素位于 2 个索引之间
- 列表和向量的索引之间的差异
- 如何删除给定索引之间的所有元素
- String_view的索引运算符([])之间的差异
- C++数组运算符和 *(数组 + 索引)之间有什么区别(如果有)
- C++递归函数,可反转数组索引在两个边界之间的顺序
- 删除 2 个给定索引之间的元素的最佳算法
- 在两个索引之间反转向量的元素
- Java和JNI (dll)之间的索引超出边界异常
- 引用和数组索引之间的任何差异