题目
思路
区间操作?线段树?想多了……由于 两个队列之间的差别很大,线段树难以维护区间信息。
没法从操作入手,我们就 从问题出发。它只问不同的数字,并且针对全局;而且每次加入都是相同的数字;又看到 a i ≤ 1 0 5 a_i\le 10^5 ai≤105 的限制,不难想到,对于每种数字求出它出现的时间段。
然而对于某一种实在太宽泛。所以我们微调一下,求出 某次操作的所有数字都被弹出 的时间。
一个数字被弹出,就是在它之后又加入了
a
i
a_i
ai 个数字嘛。用
t
i
(
x
)
t_{i}(x)
ti(x) 表示,从
x
x
x 时刻算起(不包含),到哪个时间,第
i
i
i 个队列中又加入了
a
i
a_i
ai 个数字。那么对于第
x
x
x 个修改
[
l
,
r
]
[l,r]
[l,r] 的总耗时
T
x
=
max
i
=
l
r
t
i
(
x
)
T_x=\max_{i=l}^{\;r}t_i(x)
Tx=maxi=lrti(x) 。这里是区间操作了。但是线段树仍然无能为力。线段树在这种叶子元素相去甚远的题目中就是垃圾。
区间操作除了树,就只能想到 差分 分块 了。我们可以对于每个块,求出所有数字都被弹掉的时间。对于每个块的每个
x
x
x 我们都求出来!一共也才
O
(
n
B
⋅
m
)
\mathcal O(\frac{n}{B}\cdot m)
O(Bn⋅m) 个嘛!(这里
B
B
B 是块的长度。)
具体怎么求呢?就是利用 t i ( x ) ≤ t i ( x + 1 ) t_i(x)\le t_{i}(x+1) ti(x)≤ti(x+1) 这一重要等式。我们可以 双指针,对于一个当前 l l l,移动出 r r r 使得 r = max i ∈ B l o c k t i ( l ) r=\max_{i\in Block} t_i(l) r=maxi∈Blockti(l) ,然后增大 l l l ,继续移动 r r r 。
移动 r r r 的过程就是:覆盖整个 B l o c k Block Block 的操作,直接计数;对于部分覆盖的,暴力 修改。因为部分覆盖的情况较少,一共只有 O ( m ) \mathcal O(m) O(m) 对,所以复杂度 O ( B m ) \mathcal O(Bm) O(Bm) 可以接受。(这里计数怎么判断不用讲吧?就是暴力 “重构” B l o c k Block Block 时,求一下当前状态下至少需要多少次覆盖。)移动 l l l 就是撤销 r r r 的操作,复杂度相同。
现在还剩一个问题,零散的怎么处理?如果像整块一样,直接计算贡献,好像是 O ( n m ) \mathcal O(nm) O(nm) 的?回顾一下普通的分块——哦!零散的贡献次数应当为 O ( m B ) \mathcal O(mB) O(mB) 的!那么我们只需要避免无效贡献。所以,对每个 B l o c k Block Block 都记录与它有交(但不是完全包含)的操作,设其为 ⟨ d i ⟩ \lang d_i\rang ⟨di⟩ 。此时 ∑ ∣ d i ∣ = O ( m B ) \sum |d_i|=\mathcal O(mB) ∑∣di∣=O(mB) 没有大问题。覆盖整块的操作可以先做出前缀和,否则复杂度会假。
同样的,可以对 ⟨ d i ⟩ \lang d_i\rang ⟨di⟩ 作双指针,道理和上面是一样的,只是要顺便用上前缀和数组罢了。不过 t i ( x ) t_i(x) ti(x) 并不一定是 d i d_i di 啊,也可能是某次整块覆盖操作。你傻呀?自己用 i f \tt if if 写一写就完事儿了!
复杂度 O ( m B + m n B ) ≥ O ( m n ) \mathcal O(mB+m\frac{n}{B})\ge\mathcal O(m\sqrt n) O(mB+mBn)≥O(mn),在 B = O ( n ) B=\mathcal O(\sqrt{n}) B=O(n) 时取等。