AT_diverta2019_e XOR Partitioning
先发现以下两个性质:
- 对于所有段,它们的异或和就是所有数的异或和。
- 由于每段的异或和都一样,设为 d,并设 s 为所有数的异或和,总共分成了 x 段,则有 s=[xmod2=1]d。
设 {A} 的前缀异或和为 {pre}。
分类讨论。
如果 s≠0,这个比较清晰,说明 d=s 且 x 是奇数,考虑所有 x 段的最后一个数的位置 p1,⋯,px,那么说明 prepi=[imod2=1]d,因此问题变成在 1,⋯,n 选奇数个位置,且 n 必须选,满足奇数位置上的 pre=d,偶数位置上的 pre=0。
考虑 DP,设 f(i,0/1) 表示前 i 个数,选了偶数/奇数个位置的方案数。则有
f(i,0)={f(i−1,0)ai≠0f(i−1,0)+f(i−1,1)ai=0
f(i,1)={f(i−1,1)ai≠df(i−1,1)+f(i−1,0)ai=d
否则若 s=0,继续分类讨论,若分成的段数是 x 是奇数,则说明 d=0,问题同上,否则 d 未知,问题变成在 1,⋯,n 中选偶数个位置,且 n 必须选,满足奇数位置上的 pre=d,偶数位置上的 pre=0。
如果对每个 d 都 O(n) 跑一遍 DP,将会爆炸,但是注意到转移只会在 pre=0 或 pre=d 的位置更新 f,于是预处理出所有 prei=d 的位置,然后对 prei=0 的位置进行前缀和预处理,就可以快速转移了。
时间复杂度 O(n)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500005, M = 1 << 20, mod = 1e9 + 7;
int n;
int pre[N], sum[N];
vector <int> pos[M];
bool vis[M];
void add(int &a, int b) {
a += b;
if (a >= mod) a -= mod;
}
int calc(int d) {
if (!d) {
int res = 1;
for (int i = 0; i + 1 < pos[0].size(); ++i) res = res * 2 % mod;
return res;
}
int odd = 0, even = 1, lst = 0;
for (int i = 0; i + (pre[n] == d) < pos[d].size(); ++i) {
add(even, 1ll * odd * (sum[pos[d][i]] - sum[lst]) % mod), add(odd, even);
lst = pos[d][i];
}
add(even, 1ll * odd * (sum[n - 1] - sum[lst]) % mod);
if (pre[n] == d) return even;
else return odd;
}
int main() {
scanf("%d", &n);
for (int i = 1, x; i <= n; ++i) scanf("%d", &x), pos[pre[i] = pre[i - 1] ^ x].push_back(i), sum[i] = sum[i - 1] + (pre[i] == 0);
if (pre[n]) return printf("%d", calc(pre[n])), 0;
else {
int ans = 0;
for (int i = 1; i <= n; ++i) {
if (vis[pre[i]]) continue;
vis[pre[i]] = 1, add(ans, calc(pre[i]));
}
printf("%d", ans);
}
return 0;
}
```AT_diverta2019_e
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话