1 条题解
-
0
思路与算法
设pref[j]是前j次异或和、all为总异或。插入旋转后的最终值为rot(x) XOR (rot(pref[j]) XOR all XOR pref[j])。把括号中的常量全部加入01Trie,设y=rot(x),目标转为max_y min_c(y XOR c)。Trie某位只有一个孩子时,y选相反位强制得1;有两个孩子时对手总能令该位为0,我们选两个子树后续最优值较大的方向,并在并列时累加初值数量。
正确性说明
旋转是按位线性且为n位数上的双射,所以上述代数变换不改变最优值或初值数量。XOR比较先看最高不同位:单孩子时取得1严格优于0,选择唯一;双孩子时对手可跟随y同位,使最高位为0,最坏后缀仅由该孩子决定。两分支的y首位不同,最优后缀并列时计数相加不重叠。叶子没有剩余位,恰有1种后缀选择。
复杂度
插入m+1个n位常量及遍历Trie总O(n(m+1))时间、同阶空间,前缀数组初始化O(m)。n≤30、m≤10万,最多约300万节点、每节点两个int,计数≤2^30用long long。每次只访问实际节点,重复常量不增加新选择。
易错点
旋转时刻包括异或之前与全部异或之后,不能漏pref[0]或pref[m]。求的是最坏时刻后的最大值,不是通常Trie最大异或。独立小n枚举所有初值和全部时刻;大规模两个常量及完整低16位偶校验常量族给出严格闭式值和数量。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct Node { int ch[2] = {-1, -1}; }; vector<Node> tr; pair<long long, long long> solve(int u, int bit) { if (bit < 0) return {0, 1}; int a = tr[u].ch[0], b = tr[u].ch[1]; if (a < 0 || b < 0) { auto x = solve(a < 0 ? b : a, bit - 1); x.first += 1LL << bit; return x; } auto x = solve(a, bit - 1), y = solve(b, bit - 1); if (x.first > y.first) return x; if (y.first > x.first) return y; return {x.first, x.second + y.second}; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<unsigned> prefix(m + 1); for (int i = 1; i <= m; ++i) { unsigned x; cin >> x; prefix[i] = prefix[i - 1] ^ x; } if (n == 0) { cout << 0 << '\n' << 1 << '\n'; return 0; } unsigned mask = (1U << n) - 1; auto rotate = [&](unsigned x) { return ((x << 1) & mask) | (x >> (n - 1)); }; tr.reserve(size_t(m + 1) * n + 1); tr.emplace_back(); for (unsigned p : prefix) { unsigned c = rotate(p) ^ prefix.back() ^ p; int u = 0; for (int bit = n - 1; bit >= 0; --bit) { int b = (c >> bit) & 1U; if (tr[u].ch[b] < 0) { tr[u].ch[b] = tr.size(); tr.emplace_back(); } u = tr[u].ch[b]; } } auto ans = solve(0, n - 1); cout << ans.first << '\n' << ans.second << '\n'; }
- 1