CF899F Letters Removing 题解
这好像是个典题。
解法
一个很自然的想法。
考虑开 62 62 62 颗平衡树,即每一种字符都开一颗平衡树,维护的是每种字符出现的位置,每次操作就把对应的平衡树区间删去即可,是很基础的非旋 treap 操作。
实现就是按值分裂。
inline std::pair<fhq_node *,fhq_node *> split(fhq_node *rt,const int k)
{
std::pair<fhq_node *,fhq_node *> ret;
if(rt==nullptr) return std::make_pair(nullptr,nullptr);
if(k<rt->val) ret=split(rt->lc,k),rt->lc=ret.second,rt->pushup(),ret.second=rt;
else ret=split(rt->rc,k),rt->rc=ret.first,rt->pushup(),ret.first=rt;
return ret;
}
当我搓完平衡树后发现过不了样例,仔细看样例才发现,字符的标号是随着删除操作而变化的。所以我们需要动态的维护每个数的标号,这也是很典的问题。考虑用一个树状数组,初始值都是 1 1 1,表示所有字符都没有被删除,之后每删除一个字符,我们就在树状数组的对应位置减 1 1 1,树状数组中第 k k k 大的数的位置就是删除后的第 k k k 个字符在原序列中的位置。最朴素的想法就是在树状数组上二分。
当然,有比树状数组上二分复杂度更优的做法,就是树状数组上倍增。
注意到树状数组上的节点 c i c_i ci 表示 ∑ j = i − l o w b i t ( i ) + 1 i a j \sum \limits _{j=i-lowbit(i)+1}^i a_j j=i−lowbit(i)+1∑iaj,所以我们从高位向低位枚举答案的每个二进制位即可。
inline int kth(int k)
{
int sum=0,