ABC417G - Binary Cat

题意

字符串 S 0 = 0 , S 1 = 1 S_0=0,S_1=1 S0=0,S1=1 S i = S l S r ( 0 ≤ l , r < i ) S_i=S_lS_r(0\le l,r <i) Si=SlSr(0l,r<i),让你输出 S i [ X i ] S_i[X_i] Si[Xi]

思路

虽然没有做出来,但还是想写个题解。
赛时看到以为是左偏树/可并堆,然后我一直在思考应该怎么调整这个树形结构,发现不是很可行。
赛后看了通过的玩家的代码,发现他们用了倍增,然后我就去思考倍增怎么做。

单链倍增

如果是直接维护倍增,那么时间和空间复杂度都是 O ( n log ⁡ 2 V ) O(n\log^2V) O(nlog2V),时间或许可行,但是空间一定会炸,大概思路如下:

设倍增数组 n x t [ i ] [ j ] nxt[i][j] nxt[i][j] 表示从点 i i i 往后跳 2 j 2^j 2j 后到达的结点。

我们将 S l S_l Sl S r S_r Sr 连起来以后只需要在 n x t nxt nxt 数组不同的位置新开结点即可,也就是仍在 S l S_l Sl 内的最大的 j j j,这样每次新建 O ( log ⁡ V ) O(\log V) O(logV) 个结点,一共需要开 1 0 9 10^9 109 级别的 int 和 long long,显然行不通。

树剖+倍增

学校里的大佬告诉我正确的做法用到了树剖,于是我意识到了可以用树剖降掉一个 l o g log log 的空间复杂度。

这个的原理大概就像用树剖写的最近公共祖先(lca)只有 O ( n ) O(n) O(n) 的空间复杂度,但是用倍增写的有 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的空间复杂度。因为树剖能很好的利用树形结构。

我们将单个字符全部存放在叶子结点,非叶子结点只记录子树大小。关于字符的顺序,就像平衡树那样下标小的放在左边,下标大的放在右边。

当我们令 S i = S l S r S_i=S_lS_r Si=SlSr 的时候,就让结点 l l l 作为结点 i i i 的左儿子,让结点 r r r 作为结点 i i i 的右儿子。

接下来就要连重链,也就是那个儿子的子树大小 s i z [ ] siz[] siz[] 更大,就往哪边连重链。

下一步就需要维护重链上的倍增数组,这个倍增数组是从上往下的, n x t [ i ] [ j ] nxt[i][j] nxt[i][j] 记录从结点 i i i 往下跳 2 j 2^j 2j 步到达的结点, s u m [ i ] [ j ] sum[i][j] sum[i][j] 记录从结点 i i i 开始的 2 j 2^j 2j 个重链上的结点的非重链左儿子(如果有)的子树大小之和。注意到,重链的长度最多为 O ( n ) O(n) O(n),因此第二维只用开到 20 20 20 即可。

最后就是查询 S i [ X i ] S_i[X_i] Si[Xi],总体思路就是先从重链往下跳,然后走轻儿子,然后再从重链往下跳,以此反复,直到到达结点 0 0 0 或者结点 1 1 1

code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值