题解 - AGC071A XOR Cross Over

变身喜欢降降降的小朋友。评分系统我问你,A、B、C 真的是同一个紫吗?

简述题意

给一个数列 {a1,,an}\{a_1,\ldots,a_n\}。每次可以选择一个长度不为 11 的数列拆成非空的两半:{a1,,ak1}\{a_1,\ldots,a_{k-1}\}{ak,,an}\{a_k,\ldots,a_n\},然后每个元素都异或上另一半的异或和。最小化这样拆分 n1n-1 次后的元素和。n500,ai<240n\le 500,a_i< 2^{40}

追踪

关注一下这个过程中值的变化。比如说现在有一个数列,根据题意,它们在以前被异或的总和是相同的,假设叫做 tt。再稍微假设一下分裂成的两半的原始数分别是 {a1,,an}\{a_1,\ldots,a_n\}{b1,,bm}\{b_1,\ldots,b_m\},就是说它们现在的值分别是 {a1t,,ant}\{a_1\oplus t,\ldots,a_n\oplus t\}{b1t,,bmt}\{b_1\oplus t,\ldots,b_m\oplus t\}。令 B=1imbiB=\oplus_{1\le i\le m} b_i,我们追踪 aa 中元素的变化(bb 为对称的情况):

  • mm 是偶数时,aita_i\oplus t 变为 aiBta_i\oplus B\oplus t
  • mm 是奇数时,aita_i\oplus t 变为 aiBa_i\oplus B

刻画

把分裂的过程画成线段树,aia_i 现在的值是:找到其祖先中最低的,令其为 aa',满足 aa' 的兄弟长度为奇数,aia_i 现在的值即为 aa' 父亲区间的异或和。

或者,不如说:找到其祖先中最低的,令其为 aa',满足 aa' 的长度为偶数,aia_i 现在的值即为 aa' 区间的异或和。

如果不存在 aa',必然有 nn 是奇数,手动模拟发现 aia_i 现在的值是整个序列的异或和,我们称它为“特殊点”。

区间 DP

f(l,r)f(l,r) 表示把 [l,r)[l,r) 中的区间拆到一个线段树节点下,最小的元素和。对于长为奇数的区间,不计入“特殊点”,因为它会被更新。边界情况是长度为 f(i,i+1)=0f(i,i+1)=0

枚举分界点 kk

  • 两个子树均为奇数长:f(l,r)f(l,k)+f(k,r)+2Sf(l,r)\leftarrow f(l,k)+f(k,r)+2S,其中 SS 表示 [l,r)[l,r) 区间的异或和。
  • 否则:f(l,r)f(l,k)+f(k,r)f(l,r)\leftarrow f(l,k)+f(k,r)

最后输出时处理全局中的“特殊点”。

时间复杂度 O(n3)O(n^3)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int MXN = 505;
const ll INF = 1LL << 62;
int N;
ll arr[MXN], pfs[MXN];
ll mns[MXN][MXN];

int main() {
cin >> N;
for (int i(0); i != N; ++i) cin >> arr[i], pfs[i + 1] = pfs[i] ^ arr[i];
for (int len(2); len <= N; ++len) {
for (int l(0); l + len <= N; ++l) {
int r(l + len);
ll s(pfs[r] ^ pfs[l]);
mns[l][r] = INF;
for (int i(l + 1); i != r; ++i)
mns[l][r] = min(mns[l][r], mns[l][i] + mns[i][r] +
((i - l) % 2 + (r - i) % 2 == 2 ? (2 * s) : 0LL));
}
}
cout << mns[0][N] + (N & 1 ? pfs[N] : 0LL) << endl;
return 0;
}

题解 - AGC071A XOR Cross Over
http://sunsetglow95.github.io/2025/03/31/sol-AGC071A/
作者
SunsetGlow95
发布于
2025年3月31日
许可协议