cf1540d-solution
CF1540D Solution
题意:
给你一个长度为 \(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 的吊着踩。

浙公网安备 33010602011771号