1)题目意思
有一排 n 个史莱姆,体重是 w1..wn。
现在对每个询问给一个 x:在最右边追加一个体重为 x 的新史莱姆,它会不断尝试吃掉左侧相邻史莱姆:
* 若当前体重 cur >= 左边史莱姆体重 w,则吃掉它,cur 变成 cur XOR w,并向左移动一格
* 否则停止
这一过程只影响新史莱姆,不会让原来的史莱姆互相吃。
你要输出:新史莱姆最终能吃掉多少个史莱姆。
多组测试,所有测试的 n 总和与 q 总和都不超过 2e5。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2)思路解析(对应你给的“MSB + 跳跃 + 前缀XOR”的做法,保证可过大数据)
关键结论 1:如果某个史莱姆的 MSB 比当前 X 小,一定能吃掉
设 msb(v) 是 v 的最高位位置(例如 msb(1)=0, msb(2)=1, msb(3)=1, msb(4)=2)。
若 msb(w) < msb(x),那么一定有 w < 2^{msb(x)} <= x,所以当前 x >= w,必然能吃。
关键结论 2:吃掉 MSB 更小的史莱姆,不会让 MSB(X) 变小
当 msb(w) < msb(x) 时,x XOR w 的最高位仍然保持在 msb(x)(不会被更低位的数改变掉最高位)。
所以:在 msb(x) 固定不变时,我们可以“批量吃掉一段 msb 更小的史莱姆”,只要能快速算出这段的 xor。
关键结论 3:只有吃掉“MSB 相等”的史莱姆,MSB(X) 才可能下降
当 msb(w) == msb(x) 且能吃掉时(x >= w),吃完变成 x XOR w,最高位那一位会被消掉,msb(x) 会下降到更小。
因此:msb(x) 每次真正“跨过一个障碍”都会下降,最多下降 30 次(因为数 < 2^30)。
如何快速找到“左边最近一个 MSB >= 当前 MSB(X) 的位置”
预处理数组 last[i][b]:
* 表示在下标 0..i 范围内,最后一个满足 msb(w[pos]) >= b 的位置 pos
* 如果不存在则为 -1
这样对于当前指针 idx 和当前 msb(x)=b,下一次需要处理的“关键位置”就是:
* nxt = last[idx][b]
在 (nxt+1 .. idx) 这一段中,所有史莱姆都满足 msb < b,所以它们都必吃,可以用前缀 xor 一次性算掉。
如何快速算区间 XOR
预处理前缀异或 px[i] = w0 XOR w1 XOR ... XOR w(i-1)(长度 n+1):
* 区间 (l..r) 的 xor = px[r+1] XOR px[l]
这样“批量吃掉 (nxt+1..idx)”后:
* x ^= (xor of w[nxt+1..idx])
* 然后尝试吃 w[nxt](若 x >= w[nxt] 则吃掉并 x ^= w[nxt],继续往左)
整个查询循环次数最多 O(log W) 次(大约 30 次),所以总复杂度 O((n+q)*logW)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------