「Bombs」Solution
题意简述
给定两个长度为
n
n
n 的排列
a
,
b
a ,b
a,b,对于一个初始为空的集合进行以下操作:对于每个
i
i
i,将
a
i
a_i
ai 加入集合,若
v
i
s
i
=
1
vis_i=1
visi=1,将此时集合中的最大值删除。
现对于每一个
i
i
i,求出
∀
j
∈
[
1
,
i
−
1
]
,
v
i
s
j
=
1
\forall j \in [1 , i - 1],vis_j=1
∀j∈[1,i−1],visj=1 的情况下,进行一次操作后集合中的最大值。
- 1 ≤ n ≤ 3 × 1 0 5 1 \le n \le 3 \times 10^5 1≤n≤3×105
思路
不妨令
a
n
s
i
ans_i
ansi 为
i
i
i 的答案,由于只有删除操作,显然有
a
n
s
i
+
1
≤
a
n
s
i
ans_{i+1} \le ans_i
ansi+1≤ansi。
可以很自然的想到,我们可以
O
(
n
)
O(n)
O(n) 从
n
n
n 往
1
1
1 扫一遍,对于每一个
i
i
i 判断当前枚举到的
x
x
x 合不合法,如果不合法就
x
←
x
−
1
x \leftarrow x-1
x←x−1,继续判断即可。
所以现在的瓶颈便在于:该如何快速判断当前枚举到的 x x x 合不合法呢?
- Hint1: x x x 合法意味着什么?
- Hint2:一个数 y y y 已被删除的条件是什么?
- Hint3:需要删除的数和标记点有什么联系?
不妨从以上 tips \text{tips} tips 入手。如果 x x x 合法,那么 x x x 一定未被删除,且 ∀ y ∈ [ x + 1 , n ] \forall y \in [x + 1 , n] ∀y∈[x+1,n] 都已被删除。 y y y 被删除的充要条件是 y y y 所处的位置后面有一个标记点可以用来删除 y y y。想到这,便不难发现其实判断的本质就是在寻找标记点与待删除点的 配对关系,类似于一个 括号序列。
不妨将一个待删除点 y ∈ [ x + 1 , n ] y \in [x + 1 , n] y∈[x+1,n] 视作左括号,将一个标记点视作右括号,那么原问题便转化为括号序列中,每一个 左括号是否都有与之对应的右括号 的匹配问题。
注意在此题中,一个位置上既可以有左括号,也可以有右括号(一个点既可以待删除,也可以被标记。)且此题中括号匹配存在优先级的概念,即 y ∈ [ x + 1 , n ] y \in [x+1,n] y∈[x+1,n] 会比 x x x 优先匹配右括号。通俗地讲,如果想给当前 x x x 分配右括号,那么必须先匹配完比他大的数才可以。
想想普通的括号匹配问题,如果对于任意位置 i i i, [ 1 , i ] [1 , i] [1,i] 中左括号的数量大于等于 [ 1 , i ] [1,i] [1,i] 中右括号的数量,且 [ 1 , n ] [1,n] [1,n] 中左括号与右括号序列数量相等才能匹配。对于这道题,我们不要求匹配,只需要左括号配对完即可,右括号过剩也是被允许的。因此,如果对于任意 i i i, [ i , n ] [i,n] [i,n] 中左括号的数量都小于等于 [ i , n ] [i,n] [i,n] 中右括号的数量,那么便说明 [ i , n ] [i,n] [i,n] 中的右括号不仅可以把 [ i + 1 , n ] [i+1,n] [i+1,n] 中的左括号匹配完,且可以匹配到当前 i i i 位置上的左括号。
所以, x x x 不合法(即被删除) 的充要条件为:对于任意 i i i, [ i , n ] [i,n] [i,n] 中左括号的数量小于 [ i , n ] [i,n] [i,n] 中右括号的数量。也就等价于 [ i , n ] [i,n] [i,n] 中左括号的数量减去 [ i , n ] [i,n] [i,n] 中右括号的数量小于等于 0 0 0。所以,我们将左括号视为 1 1 1,右括号视为 − 1 -1 −1,用线段树维护这个差值即可,添加括号就修改后缀,最后判断全局最大值 M a x Max Max 是否 ≤ 0 \le0 ≤0 即可。
代码
代码也很简洁易懂。
#include<bits/stdc++.h>
#define int long long
const int MAXN = 3e5 + 5;
using namespace std;
int n , a[MAXN] , b[MAXN] , id[MAXN];
namespace Segment{
struct tree{
int l , r , Max , val , add;
}tree[MAXN << 3];
void pushup(int p) {tree[p].Max = max(tree[p << 1].Max , tree[p << 1 | 1].Max);}
void pushdown(int p) {
if (tree[p].add) {
tree[p << 1].Max += tree[p].add , tree[p << 1 | 1].Max += tree[p].add;
tree[p << 1].add += tree[p].add , tree[p << 1 | 1].add += tree[p].add;
tree[p].add = 0;
}
}
void build(int p , int l , int r) {
tree[p].l = l , tree[p].r = r;
if (l == r) return;
int mid = l + r >> 1;
build(p << 1 , l , mid) , build(p << 1 | 1 , mid + 1 , r);
}
void update(int p , int l , int r , int v) {
if (tree[p].l >= l && tree[p].r <= r) {
tree[p].Max += v , tree[p].add += v;
return;
}
pushdown(p);
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid) update(p << 1 , l , r , v);
if (r > mid) update(p << 1 | 1 , l , r , v);
pushup(p);
}
}
using namespace Segment;
bool check(int x) {return (tree[1].Max <= 0);}
void remove(int x) {update(1 , 1 , id[x] , 1);}
void add(int x) {update(1 , 1 , x , -1);}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
cin >> n;
build(1 , 1 , n);
for (int i = 1 ; i <= n ; i ++) cin >> a[i] , id[a[i]] = i;
for (int i = 1 ; i <= n ; i ++) cin >> b[i];
remove(n);
for (int i = 1 , now = n ; i <= n ; i ++) {
while(now >= 1) {
if (check(now)) remove(-- now);
else break;
}
cout << now << ' ';
add(b[i]);
}
return 0;
}

浙公网安备 33010602011771号