cf1540d-solution

CF1540D Solution

link

题意:

给你一个长度为 \(n\) 的序列 \(a\),支持以下操作:

1 x y:令 \(a_x\gets y\)

2 x:构造 \(1\sim n\) 的排列 \(p\),满足 \(\forall i\in[1,n]\)\(p_1\sim p_{i-1}\) 恰有 \(a_i\) 个数大于 \(p_i\)。求 \(p_x\)


为方便,将 \(a_i\gets i-a_i\),这样 \(a_i\) 就是 \(p_1\sim p_i\)\(p_i\) 的排名。

手玩一下构造。假设有 \(a=\{1,2,2,1,3\}\),从 \(1\)\(5\) 逐个构造 \(p_i\)

a : 1 2 2 1 3
p : 1
p : 1 2
p : 1 3 2
p : 2 4 3 1
p : 2 5 4 1 3

你发现每次构造 \(p_i\) 时就是将 \(p_i\gets a_i\),将前面所有 \(\ge a_i\)\(p\) 全部 \(+1\)

这里有重要性质:每次构造只会影响前面已构造的 \(p\),不会影响到后面还没构造的 \(p\)

回到原题,题目每次要求 \(p_x\),那么根据上面的性质,我们直接从 \(p_x\) 开始构造,并计算构造后面 \(p\) 时对 \(p_x\) 的影响。

那么询问转化为,初始 \(ans=p_x\),然后从 \(x+1\) 枚举到 \(n\),如果 \(ans\ge a_i\)\(ans\gets ans+1\)

考虑序列分块,块长为 \(B\)。我们尝试在每块内维护 \(nxt_x\) 表示数 \(x\) 从块左端点走到右端点会增加多少。

那么由于块长为 \(B\)\(nxt\) 的值全部都 \(\le B\) 且单调不降。

\(nxt\) 可以被看做一个只有 \(B\) 种取值的分段函数,函数值分别为 \(1,2,3,\cdots,B\)

我们直接维护块内的分段函数的 \(B\) 个自变量边界,询问暴力跳块用 upper_bound 询问函数值即可。

最后处理单点修改。考虑每块开线段树,线段树结点维护它对应区间的分段函数边界,push_up 直接线性归并即可。

这样单点修改复杂度是 \(\displaystyle\sum_{i}\frac B{2^i}=\mathcal O(B)\) 的。

复杂度是 \(\mathcal O(n(B+\frac n B\log n))\) 的,取 \(B=\sqrt{n\log n}\),最终时间 \(\mathcal O(n\sqrt{n\log n})\),空间 \(\mathcal O(n\log n)\)

听说可以分散层叠 \(\mathcal O(n\sqrt n)\)(?


upd:好像不用分散层叠也可以 \(\mathcal O(n\sqrt n)\)

具体地,把询问和修改塞到每个块中,离线从左到右逐块处理,

每个块的修改操作将询问分成若干段,每段内线段树根节点的形态是一样的。于是可以双指针直接扫。

注意双指针之前要把同一段中的询问按照它本来的 \(ans\) 排序,这个排序用快排复杂度会退化,用计数排序即可。

还有为了做到线性空间,我们在塞询问时只把询问塞到最左边的块,然后处理完这个块把询问塞到下一个块中。

时间复杂度是 \(\mathcal O(n\sqrt n)\),空间 \(\mathcal O(n)\)。然而常数过大被带 log 的吊着踩。

posted @ 2024-02-28 13:38  iorit  阅读(12)  评论(0)    收藏  举报