当前位置: 首页 > news >正文

异或区间的划分

给定一个长度为 n 的整数数组 nums[1..n],求有多少种划分方式可以将它划分为若干个连续区间,使得每个区间的异或值都相同。

异或的一些性质

对于二进制

0 ^ 0 = 0  
1 ^ 0 = 1  
0 ^ 1 = 1  
1 ^ 1 = 0

相同为0,不同为1

满足交换律、结合律:

  • a ⊕ b ⊕ c = a ⊕ c ⊕ b

  • a ⊕ a = 0

  • a ⊕ 0 = a

交换律: a ^ b = b ^ a


结合律:(a ^ b) ^ c = a ^ (b ^ c)

自反性 / 恒等律:a ^ 0 = a

自异性 / 自消律:a ^ a = 0
 

for (int i = 1; i <= n; i++) 
{b[i] = b[i - 1] ^ nums[i];
}
  • nums[i]:存储第 i 个输入的数字,数组从下标 1 开始。

  • b[i]:前缀异或数组。

  • b[i] = nums[1] ^nums[2] ... ^ nums[i]

根据自消率,当i>j时 ,b[i] ^ b[j]表示为 nums[j+1] ^ nums[j+2] ^ ... ^ nums[i]

b[j]表示为  nums[1] ^ nums[2] ^ ... ^ nums[j]

b[i]表示为  nums[1] ^ nums[2] ^ ... ^ nums[j]^ ...^ nums[i-1] ^nums[i]

枚举所有可能的目标值target

unordered_set<int> possible_targets;
for (int i = 1; i <= n; i++) {possible_targets.insert(b[i]);
}

异或区间的候选值

只考虑了前缀异或值作为 target 值候选,因为任何合法划分中,一段可能的异或值 target,至少得在某次 b[i] ^ b[j] 中出现过,而当 j == 0,它就是 b[i]

再且不计,一段合理的划分,第一段区间异或值就是b[i]

使用 unordered_set 去重,每种可能的区间异或值只遍历一次。

对每个 target 值做一次动态规划计算方案数

long long ans = 0;
for (int target : possible_targets) {vector<long long> dp_target(n + 1, 0);dp_target[0] = 1;

dp_target[i]:表示前 i 个数字中,以 i 为结尾、满足当前 target 的划分方案数

初始 dp_target[0] = 1,表示空序列有一种合法划分(啥都不划)。

在我理解应该是在序列开头划一刀

/ 1 2 1 2 1

for (int i = 1; i <= n; i++) {for (int j = 0; j < i; j++) {if (i == n && j == 0) continue; // 防止整体作为一段被重复计算//有意忽略“整段都不划分”的情况,防止它重复计数,但如果想把“整体一段”算进去,应该删掉它。if ((b[i] ^ b[j]) == target) {dp_target[i] = (dp_target[i] + dp_target[j]) % MOD;}}
}
ans = (ans + dp_target[n]) % MOD;
  • 判断是否可以将 [j+1..i] 作为一段,并且这段的异或值为 target

  • 如果是,那就可以从 dp_target[j] 转移到 dp_target[i]

  • 最终 dp_target[n] 表示从 1n 形成合法划分的方案数。

#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;int main() {int n;cin >> n;vector<int> nums(n + 1, 0);// b[i] = nums[1] ^ nums[2] ^ ... ^ nums[i]vector<int> b(n + 1, 0); // b[i] 表示 nums[1..i] 的前缀异或vector<long long> dp(n + 1, 0); // dp[i] 表示以 i 结尾的合法划分方案数// 输入序列for (int i = 1; i <= n; i++) {cin >> nums[i];}// 计算前缀异或for (int i = 1; i <= n; i++) {b[i] = b[i - 1] ^ nums[i];}// 初始化dp[0] = 1; // 空序列有一种划分方案// 使用 unordered_set 去重 ,存储所有可能的目标 区间 异或值unordered_set<int> possible_targets;for (int i = 1; i <= n; i++) {possible_targets.insert(b[i]); // 将每个前缀异或值添加到集合中}// 对每个可能的 异或值目标 进行 DPlong long ans = 0;for (int target : possible_targets) {vector<long long> dp_target(n + 1, 0);dp_target[0] = 1;for (int i = 1; i <= n; i++) {for (int j = 0; j < i; j++) {if(i==n&&j==0)continue;//跳过了只考虑整个数组作为一段的情况 // b[i] ^ b[j] 即为 nums[j+1..i] 的异或值if ((b[i] ^ b[j]) == target) // [j+1 : i]   b[i] ^ b[j] 即为 nums[j+1..i] 的异或值{dp_target[i] = (dp_target[i] + dp_target[j]) % MOD;}}}ans = (ans + dp_target[n]) % MOD;}cout << ans << endl;//cout << ans + 1  << endl;//考虑整个数组作为一段,需要加1return 0;
}

示例;输入 5

序列 为  1 2 1 2 1

异或前缀和  b[ ]  =  [0, 1, 3, 2, 0, 1]

target = 0,1,2,3

模拟区间异或 target = 1

i15 遍历,逐步更新 dp_target[i]

dp_target[0] = 1

/ 1 2 1 2 1

[1 2 ] 1 2 1  表示为   取 [ x x x ]此区间的异或运算

j < i

i = 1:
  • 取target 为 b[1] = 1

  • 对于所有 j 使得 b[i] ^ b[j] == 1 :   [ j+1 : i ]     

    • j = 0

    • b[1] ^ b[0] = 1 ^ 0 = 1  ✅   [ 1 ]   2 1 2 1

    • 满足条件的 j,因此 dp_target[1] += dp_target[0] = 1

    • / 1 /  2 1 2 1

    • dp_target[1] = 1

i = 2
  • b[2] = 3

  • 对于所有 j 使得 b[i] ^ b[j] == 1 

    • j = 0

    • b[2] ^ b[0] = 3 ^ 0 = 3 ≠ 1   [1 2 ] 1 2 1
      不符合条件

    • j = 1

    • b[2] ^ b[1] = 3 ^ 1 = 2 ≠ 1
      没有更多满足条件的 j,因此 dp_target[2] = 0

i = 3:
  • b[3] = 2

  • 对于所有 j 使得 b[i] ^ b[j] == 1 

    • j = 0          1 2 1 ] 2 1

    • b[3] ^ b[0] = 2 ^ 0 = 2 ≠ 1

    • j = 1        1 [ 2 1 ]2  1

    • b[3] ^ b[1] = 2 ^ 1 = 3 ≠ 1

    • j = 2       1 2  [ 1 ]  2  1

    • b[3] ^ b[2] = 2 ^ 3 = 1 ✅

    • 这时,dp_target[3] += dp_target[2] = 0

    • 没有更多满足条件的 j,因此 dp_target[3] = 0

i = 4:
  • b[4] = 0

  • 对于所有 j 使得 b[i] ^ b[j] == 1 

    • j = 0             [1 2 1  2 ] 1

    • b[4] ^ b[0] = 0 ^ 0 = 0 ≠ 1

    • j = 1        1 [ 2 1  2 ]  1

    • b[4] ^ b[1] = 0 ^ 1 = 1 ✅

    • dp_target[4] += dp_target[1] = 1

    • / 1 /  2 1 2  / 1

    • j = 2     1 2 [ 1  2 ]  1

    • b[4] ^ b[2] = 0 ^ 3 = 3 ≠ 1
       

    • j = 3

    • b[4] ^ b[3] = 0 ^ 2 = 2 ≠ 3
      因此 dp_target[4] = 1

    • / 1 /  2 1 2  / 1

i = 5:
  • b[5] = 1

  • 对于所有 j 使得 b[i] ^ b[j] == 1 

    • j = 0 

    • b[5] ^ b[0] = 1 ^ 0 = 1 ✅

    • 如果考虑整段划分,也就是不划分

    • dp_target[5] += dp_target[0] = 1

    • / 1  2 1 2  1 /

    • 程序中跳过了这种情况

    • j = 1 

    • b[5] ^ b[1] = 1 ^ 1 = 0 ≠ 1

    • j = 2 

    • b[5] ^ b[2] = 1 ^ 3 = 2 ≠ 1

    • j = 3

    • b[5] ^ b[3] = 1 ^ 2 = 3 ≠ 1

    • j = 4

    • b[5] ^ b[4] = 1 ^ 0 = 1 ✅

    • 这时,dp_target[5] += dp_target[4] = 1

    • / 1  / 2 1 2 /  1 /

    • 因此 dp_target[5] = 1

当区段异或值 区间目标值 target =   1 ,存在一种划分方法 :/ 1  / 2 1 2 /  1 /

如果 考虑整段 ++ dp_target[5] = 2 就会有两种划分情况

/ 1  / 2 1 2 /  1 /

/ 1  2  1  2  1 /

示例2:

  • b[ ] = [0, 1, 3, 2, 0, 1]

  • target = 3

i = 1
  • b[1] = 1

  • 对于所有 j 使得 b[i] ^ b[j] == 3

    • b[1] ^ b[0] = 1 ^ 0 = 1 ≠ 3   [1]  2 1 2 1
      没有满足条件的 j,因此 dp_target[1] = 0

    • / 1 2 1 2 1

2. i = 2:
  • b[2] = 3

  • 对于所有 j 使得 b[i] ^ b[j] == 3

    • b[2] ^ b[0] = 3 ^ 0 = 3 ✅  [1 2] 1 2 1
      这时,dp_target[2] += dp_target[0] = 1

    •  / 1 2 /  1 2 1

    • b[2] ^ b[1] = 3 ^ 1 = 2 ≠ 3  1 [ 2 ]  1 2 1
      没有更多满足条件的 j,因此 dp_target[2] = 1

3. i = 3:
  • b[3] = 2

  • 对于所有 j 使得 b[i] ^ b[j] == 3

    • b[3] ^ b[0] = 2 ^ 0 = 2 ≠ 3   [1 2 1 ]  2 1

    • b[3] ^ b[1] = 2 ^ 1 = 3 ✅   1 [2 1] 2 1
      这时,dp_target[3] += dp_target[1] = 0

    • b[3] ^ b[2] = 2 ^ 3 = 1 ≠ 3   1 2  [1]  2 1
      没有更多满足条件的 j,因此 dp_target[3] = 0

4. i = 4:
  • b[4] = 0

  • 对于所有 j 使得 b[i] ^ b[j] == 3

    • b[4] ^ b[0] = 0 ^ 0 = 0 ≠ 3   [1 2 1   2] 1

    • b[4] ^ b[1] = 0 ^ 1 = 1 ≠ 3   [ 2 1   2] 1

    • b[4] ^ b[2] = 0 ^ 3 = 3 ✅    1 2 [ 1   2] 1
      这时,dp_target[4] += dp_target[2] = 1

    • /1 2  / 1   2 /  1

    • b[4] ^ b[3] = 0 ^ 2 = 2 ≠ 3  1 2  1  [ 2 ] 1
      因此 dp_target[4] = 1

5. i = 5:
  • b[5] = 1

  • 对于所有 j 使得 b[i] ^ b[j] == 3

    • b[5] ^ b[0] = 1 ^ 0 = 1 ≠ 3  [ 1 2 1   2  1 ]

    • / 1 2 1 2 1 /

    • 整段划分 程序出跳过

    • b[5] ^ b[1] = 1 ^ 1 = 0 ≠ 3   1[ 2 1   2  1 ]

    • b[5] ^ b[2] = 1 ^ 3 = 2 ≠ 3    1 2 [1   2  1 ]

    • b[5] ^ b[3] = 1 ^ 2 = 3 ✅    1 2 1  [ 2  1 ]
      这时,dp_target[5] += dp_target[3] = 0

    • b[5] ^ b[4] = 1 ^ 0 = 1 ≠ 3   1 2 1   2  [ 1 ]
      因此 dp_target[5] = 0

target = 3 的情况下,最终没有有效的划分方案。

18413

相关文章:

  • CmStick CmStick ME|精工品质与工业级安全的加密狗之选[特殊字符][特殊字符]
  • CTF web入门之SQL注入使用工具sqlmap
  • 基于Spring Boot+微信小程序的智慧农蔬微团购平台-项目分享
  • 国产仪器进化论:“鲁般号”基于无人机的天线测试系统
  • idea使用docker插件一键部署项目
  • 2025年一站式AI创作平台主要功能介绍及使用教程
  • Idea中实用设置和插件
  • 第一章:自然语言处理
  • RAG应用过程监控系统选型:LangFuse
  • 使用tabs组件搭建UI框架
  • 4月21日日记
  • 基于MuJoCo物理引擎的机器人学习仿真框架robosuite
  • Python+CoppeliaSim+ZMQ remote API控制机器人跳舞
  • 服务器监控软件推荐
  • SpringBoot中PDF处理完全指南
  • 报错 | 配置 postcss 出现 报错:A `require()` style import is forbidden.
  • Android Studio 国内镜像使用与 SDK 下载速度优化指南
  • 华为盒式交换机堆叠配置
  • C语言高频面试题——指针数组和数组指针
  • idea30天使用无限使用
  • 临清农商行回应监管处罚:系2023年问题,已经进行了整改
  • 海港主场不敌蓉城遭遇联赛首败,好消息是武磊终于复出了
  • 河南社旗县委书记张荣印转任南阳市人大常委会农工委主任
  • 嵩山少林风景区女游客进男厕:不能止步于批评
  • 凭春晚分会场爆火的无锡,为何请来了上海主流媒体和网络大V
  • 江西一季度GDP为7927.1亿元,同比增长5.7%