[AGC071A] XOR Cross Over 做题笔记

寻常歌

VP了这场AGC,得到了零分......

题意简述

题目太长了

解法

观察性质。
对于一个数列 \(\{a_l\oplus B, a_{l+1}\oplus B, ......, a_r\oplus B\}\),如果将其分为 \([l, m]\)\([m+1, r]\),设 \([l, m]\) 那个数列是: \(\{a_l\oplus C, a_{l+1}\oplus C, ......, a_m\oplus C\}\)
那么有

\[C= \begin{cases} \bigoplus_{i=m+1}^{r}a_i, r-m\text{是奇数}\\ B\oplus \bigoplus_{i=m+1}^{r}a_i, r-m\text{是偶数} \end{cases} \]

一个结论是某一时刻产生了一个数列 \(\{a_l\oplus B, a_{l+1}\oplus B, ......, a_r\oplus B\}\)
那么一定存在 \(a, b\),满足 \(B=\bigoplus_{i=a}^{l-1}a_i\oplus\bigoplus_{i=r+1}^{b}a_i\),证明显然。
利用这个方法可以设计 \(O(n^5)\) 的 DP,定义是 \(F_{a, l, r, b}\) 表示对数列 \(\{a_l\oplus B, a_{l+1}\oplus B, ......, a_r\oplus B\}\)(\(B\) 的含义如上)做操作后得到的数字和的最小值。

考虑这个 DP 慢的原因在于需要将 \(O(n^2)\) 的状态用于表述 \(B\) 的值,但事实上,\(B\) 的值仅仅在 \(l=r\)(即统计贡献时)用到,所以考虑更换贡献统计方法。

为此,我们愿意建出这颗分裂树(是一颗二叉树,每个节点代表一个区间,儿子表示他分裂出的两个区间)。考虑某个叶节点最终会是哪些数的异或和,发现可以从一个叶节点 \(u\) 出发,不断移动到其父亲,直到它有一个长度为奇数的兄弟,设此时这个节点的父亲 \(p\) 为区间 \([l, r]\),那么 \(u\) 最终的值是 \(\bigoplus_{i=l}^ra_i\)

知道这一点后,考虑在 \(p\) 处统计 \(u\) 的贡献,便能省去记录 \(a, b\) 这两维度。

\(f_{i, j}\) 表示通过最优的划分方案,\([i, j]\) 这一分裂树上节点的子树的各点贡献和的最大值。

注意到一个长度为偶数的区间,它的子树内所有叶节点的贡献都一定在这个子树内被统计到,对于奇数区间,一定恰有一个叶节点的贡献没有在这个子树内被统计到。(数学归纳法不难证明)

于是有

\[f_{i, j}= \begin{cases} \min_{k=i}^{j-1}\{f_{i, k}+f_{k+1, i}\}, j-i+1\text{是奇数}\\ \min\{(\bigoplus_{t=i}^ja_t)\times 2+\min_{k-i+1\text{是奇数}}(f_{i, k}+f_{k+1, j}), \min_{k-i+1\text{是偶数}}(f_{i, k}+f_{k+1, j})\}, j-i+1\text{是偶数}\\ \end{cases} \]

直接转移即可。需要注意的一点是如果 \(n\) 是奇数,有一个点没有被统计到,而这个点的贡献一定是所有数的异或和,把他加上即可。

总结

第一步的性质是显然的。关键是 DP 的优化。
在初步的 DP 中,\(i, j\) 显然是重要的,那么为了消解掉不太重要的 \(a, b\),必须更换统计贡献的方式,因此考虑最终的每个数由哪些数异或而来。实际上,哪怕不从优化 DP 的角度,考虑这一点也是自然的。

但是仅仅得出这一不结论依然无法起到优化效果。所以这道题提供了一个 DP 优化的技巧:转移贡献统计位置。通过这样,就能设计出优秀的 DP 状态及其转移。

Code


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for(ll i=a;i<=b;i++)
const ll N=509, INF=1e18;
ll f[N][N], n, a[N], yih[N];
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin >> n;
    rep(i, 1, n)cin >> a[i];
    rep(i, 1, n)yih[i]=yih[i-1]^a[i];
    rep(len, 2, n){
        rep(i, 1, n-len+1){ll j=i+len-1;
            f[i][j]=INF;
            if((j-i+1)&1)
                rep(k, i, j-1)f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]);
            else
                rep(k, i, j-1)f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]+((k-i+1)%2==1?2*(yih[j]^yih[i-1]):0));
        }
    }
    cout << f[1][n]+((n&1)?yih[n]:0);
    return 0;
}


posted @ 2025-04-11 18:07  yanzihe  阅读(14)  评论(0)    收藏  举报