酷代码 AI
菜单
全部AI  /  热门  /  AI写代码神器
服务商

给定一个长度为 n 的序列 \(a[1...n]\),定义函数 \(f(b[1...m])\) 的值为在 \([0, m - 1]\) 内满足如下条件的 i 的数目:b 中前 i 个数异或起来的值小于 b 中前 \(i + 1\) 个数异或起来的值。对于 \(a[1...n]\) 的每个子序列 \(b[1...m]\),求 \(f(b[1...m])\) 之和,答案对 998244353 取模。

问题重述 给定一个长度为 \( n \) 的序列 \( a[1...n] \),定义函数 \( f(b[1...m]) \) 的值为在 \([0, m - 1]\) 内满足如下条件的 \( i \) 的数目:\( b \) 中前 \( i \) 个数异或起来的值小于 \( b \) 中前 \( i + 1 \) 个数异或起来的值。对于 \( a[1...n] \) 的每个子序列 \( b[1...m] \),求 \( f(b[1...m]) \) 之和,答案对 998244353 取模。 问题分析 我们需要计算所有子序列的 \( f(b) \) 之和。直接枚举所有子序列显然不可行,因为子序列的数量是 \( 2^n \),对于 \( n \) 较大的情况无法处理。我们需要找到一种数学上的性质或模式,能够高效地计算这个总和。 关键观察 1. 异或前缀和的性质:设 \( s_i \) 是子序列 \( b \) 的前 \( i \) 个元素的异或和(即 \( s_i = b_1 \oplus b_2 \oplus \dots \oplus b_i \)),则 \( f(b) \) 统计的是满足 \( s_i < s_{i+1} \) 的 \( i \) 的数量。 - 注意到 \( s_{i+1} = s_i \oplus b_{i+1} \),因此 \( s_i < s_{i+1} \) 等价于 \( s_i < s_i \oplus b_{i+1} \)。 - 这是一个关于异或的经典性质:对于非负整数 \( x \) 和 \( y \),\( x < x \oplus y \) 当且仅当 \( y \) 的最高位在 \( x \) 的对应位为 0。 2. 逐位贡献:我们可以考虑每一位对总和的贡献。具体来说,对于第 \( k \) 位(从最高位到最低位),计算有多少个子序列中的元素 \( b_{i+1} \) 满足: - \( b_{i+1} \) 的第 \( k \) 位是 1。 - 前 \( i \) 个元素的异或和的第 \( k \) 位是 0。 - 这样 \( b_{i+1} \) 会贡献一个满足 \( s_i < s_{i+1} \) 的 \( i \)。 3. 动态规划或组合数学:我们需要设计一个方法,能够高效地计算对于每一位 \( k \),在所有子序列中满足上述条件的 \( (b_{i+1}, i) \) 对的数量。 具体思路 - 对于每一位 \( k \)(假设从 0 到 30,因为 \( a_i \) 是 32 位整数),我们独立计算该位的贡献。 - 对于固定的 \( k \),我们需要计算所有子序列中满足 \( b_{i+1} \) 的第 \( k \) 位为 1 且前 \( i \) 个元素的异或和的第 \( k \) 位为 0 的 \( (b_{i+1}, i) \) 对的数量。 - 这可以通过以下方式计算: - 对于序列中的每个元素 \( a_j \),如果它的第 \( k \) 位为 1,那么它可以作为 \( b_{i+1} \) 贡献到某些子序列中。 - 我们需要计算在 \( a_j \) 之前(在子序列中)的元素中,异或和的第 \( k \) 位为 0 的情况数。 - 这可以通过动态维护一个状态:当前异或和的第 \( k \) 位是 0 或 1 的子序列数。 动态维护 - 设 \( dp0 \) 和 \( dp1 \) 分别表示当前异或和的第 \( k \) 位为 0 和 1 的子序列数(不包括当前元素)。 - 初始时 \( dp0 = 1 \)(空子序列的异或和为 0,第 \( k \) 位为 0),\( dp1 = 0 \)。 - 对于每个元素 \( a_j \): - 如果 \( a_j \) 的第 \( k \) 位为 0: - 新的 \( dp0' = dp0 + dp0 \)(选或不选 \( a_j \) 都不改变第 \( k \) 位)。 - 新的 \( dp1' = dp1 + dp1 \)。 - 如果 \( a_j \) 的第 \( k \) 位为 1: - 新的 \( dp0' = dp0 + dp1 \)(选 \( a_j \) 会翻转第 \( k \) 位)。 - 新的 \( dp1' = dp1 + dp0 \)。 - 如果 \( a_j \) 的第 \( k \) 位为 1,那么它可以作为 \( b_{i+1} \) 贡献 \( dp0 \) 到答案中(因为前 \( i \) 个元素的异或和的第 \( k \) 位为 0 的子序列数是 \( dp0 \))。 总和计算 - 对于每一位 \( k \),计算其贡献 \( c_k \) 为所有 \( a_j \) 的第 \( k \) 位为 1 时的 \( dp0 \) 之和。 - 总答案是 \( \sum_{k} c_k \cdot 2^k \)。 代码实现 ```python MOD = 998244353 def solve(): import sys n, *rest = map(int, sys.stdin.read().split()) a = rest[:n] total = 0 for k in range(31): # 处理每一位 mask = 1 << k dp0 = 1 # 初始空子序列的异或和为0,第k位为0 dp1 = 0 cnt = 0 for num in a: if num & mask: # a_j的第k位为1 # 贡献是之前的dp0 cnt += dp0 # 更新dp0和dp1 new_dp0 = (dp0 + dp1) % MOD new_dp1 = (dp1 + dp0) % MOD dp0, dp1 = new_dp0, new_dp1 else: # a_j的第k位为0 new_dp0 = (dp0 + dp0) % MOD new_dp1 = (dp1 + dp1) % MOD dp0, dp1 = new_dp0, new_dp1 total = (total + cnt * (1 << k)) % MOD print(total) solve() ``` 代码解释 1. 输入处理:读取序列长度 \( n \) 和序列 \( a \)。 2. 逐位处理:对于每一位 \( k \)(从 0 到 30): - 初始化:`dp0` 表示异或和第 \( k \) 位为 0 的子序列数,初始为 1(空子序列);`dp1` 表示异或和第 \( k \) 位为 1 的子序列数,初始为 0。 - 遍历序列:对于每个元素 \( a_j \): - 如果 \( a_j \) 的第 \( k \) 位为 1,则它可以贡献 `dp0` 到答案中(因为前 \( i \) 个元素的异或和的第 \( k \) 位为 0),然后更新 `dp0` 和 `dp1`(选或不选 \( a_j \) 会翻转第 \( k \) 位)。 - 如果 \( a_j \) 的第 \( k \) 位为 0,则直接翻倍 `dp0` 和 `dp1`(选或不选 \( a_j \) 不改变第 \( k \) 位)。 - 累加贡献:将当前位的贡献 \( cnt \cdot 2^k \) 加到总和中。 3. 输出结果:打印总和模 998244353 的结果。 额外注意的逻辑点 1. 空子序列的处理:空子序列的异或和为 0,其所有位都为 0,因此初始时 `dp0 = 1`。 2. 模运算的一致性:所有中间结果都需要对 998244353 取模,以防止溢出。 3. 位运算的正确性:确保每一位的处理是独立的,且 \( 2^k \) 的计算不会溢出(Python 的整数不会溢出,但取模是必要的)。 [2025-04-26 10:22:18 | AI写代码神器 | 1885点数解答]

相关提问