题意简述
一棵高度为 n 的满二叉树(节点数为 2n+1−1),用一个 [0,2n) 的数 X 表示一条根到叶子的路径(从最高位开始,为0
则走左儿子,否则走右儿子)。
初始 X=0,有两种操作共 q 个:
- 将 X 改为 (X+2C)mod2n,然后从根出发走到叶子。
- 询问当前有多少个节点至少被访问一次。
n,q≤105。
主要思路
考虑一次新增的节点数,不难发现是 n 减去新的 X 与之前所有数的 lcp 的最大值。
不难发现这个最大值必然在新 X 的前驱或后继中取得。
考虑加上 2C 的操作,实质上是把前面的一段1
赋为0
,把一个0
赋为1
。
于是每个修改操作只会改变均摊 O(1) 个位置。
于是可以上棵可持久化线段树维护每个位的值。
那么如何比较大小?
可以在可持久化线段树上二分第一个两数不同的位置,然后 O(logn) 比较。
那么我们希望能够 O(1) 比较两棵线段树上的一个区间是否完全相等。
这个哈希一下就好了。
于是我们获得了一个 O(logn) 比较大小的方法,再套个set
维护前驱后继就 O(nlog2n) 走了。
然后这题被出到模拟赛里有人被卡常了,所以是 CC 太快还是 CC 数据水啊(
参考代码
1 | #include <bits/stdc++.h> |