Galois_Field_1048576
2024-11-17 13:52:01
一篇注重证明的题解。
Nimber 积科普题。首先,不难看出这是一道线段树题。我们在此列出作为一个线段树能维护的代数结构:
两个幺半群
M_1, M_2 ,附加幺半群同态M_1 \to \mathrm{End}_{\sf Mon}(M_2) 。此时,M_1 被称为 lazytag 类型,M_2 被称为节点类型。换句话说:你的节点类型存在一种拼接组合的方式,它满足结合律,且存在一个 identity tag
e 使得f \cdot e = e \cdot f = f ;你的 lazytag 类型f \in M_1 应该满足结合律、且存在一个不进行作用的 tag(幺元),并且可以将其作用在一个节点m \in M_2 上,得到f(m) 。这样的节点是可以分裂的,也就是说如果m = m_1 \cdot m_2 ,则f(m) = f(m_1) \cdot f(m_2) 。
后文,记
同时
观察 Nimber 积的定义发现,如果我们按照 von Neumann 的自然数构造,认为
考虑 Nimber 积
的代数性质:
(证明均为展开定义。)另外,我们暂且承认若
定理. 设
证明. 设
定理.
证明. 引入概念:称
于是断言:
证明:根据 Möbius 反演的乘性版本,有
所以
引入符号
现在我们证明更强的命题:
根据 Gauß 引理,
现在,我们设
所以,如果我们设 Nimber 上的
由于两边相等,所以不等号取等,所以
注:
推论 (Fermat 小定理). 设
现在,回归题目。在题目中,我们每次操作进行一次平方。这样,平方
于是,我们得到了一个问题:如何切实地计算 Nimber 平方?解法是:观察到平方是线性的,也就是说
这样我们只需要预处理出
补充证明. 我们还没有证明若
设我们已知
这一定理被称为扩张定理。
代码环节:
#include <bits/stdc++.h>
using namespace std;
/* code of lazy_segtree copied from Atcoder Library. */
namespace Nimber {
using nim = unsigned int;
array<nim, 32> square_table = {
1u, 3u, 6u, 13u, 24u, 52u,
103u, 222u, 384u, 832u, 1648u, 3552u,
6237u, 13563u, 26511u, 56906u, 98304u, 212992u,
421888u, 909312u, 1596672u, 3472128u, 6786816u, 14567936u,
25190110u, 54589881u, 108036850u, 232800673u, 408783316u, 888883132u,
1737454078u, 3729449897u};
nim square(nim x) {
nim ret = 0;
for (int i = 0; i < 32; i++)
if (x & (1u << i)) ret ^= square_table[i];
return ret;
}
} // namespace Nimber
namespace Used_for_lazyseg {
const size_t Repetition = 32;
struct Node {
array<int64_t, Repetition> sum;
array<Nimber::nim, Repetition> nim_sum;
};
Node merge(Node a, Node b) {
Node ret;
transform(a.sum.begin(), a.sum.end(), b.sum.begin(), ret.sum.begin(),
plus<>());
transform(a.nim_sum.begin(), a.nim_sum.end(), b.nim_sum.begin(),
ret.nim_sum.begin(), bit_xor<>());
return ret;
}
Node identity_nd() { return {{}, {}}; }
using Lazy = size_t;
Lazy composite(Lazy a, Lazy b) { return (a + b) % Repetition; }
Lazy identity_lz() { return 0; }
Node apply(Lazy a, Node b) {
rotate(b.sum.begin(), b.sum.begin() + a, b.sum.end());
rotate(b.nim_sum.begin(), b.nim_sum.begin() + a, b.nim_sum.end());
return b;
}
Node get(Nimber::nim x) {
array<int64_t, Repetition> sum;
array<Nimber::nim, Repetition> nim_sum;
sum[0] = nim_sum[0] = x;
for (int i = 1; i < Repetition; i++)
sum[i] = nim_sum[i] = Nimber::square(sum[i - 1]);
return {sum, nim_sum};
}
using Seg =
lazy_segtree<Node, merge, identity_nd, Lazy, apply, composite, identity_lz>;
} // namespace Used_for_lazyseg
int main() {
int n, q;
cin >> n >> q;
vector<Nimber::nim> a(n);
for (auto& i : a) cin >> i;
Used_for_lazyseg::Seg seg(n);
for (int i = 0; i < n; i++) seg.set(i, Used_for_lazyseg::get(a[i]));
while (q--) {
int op, x, y;
cin >> op >> x >> y;
x--;
if (op == 1)
seg.apply(x, y, 1);
else if (op == 2)
cout << seg.prod(x, y).nim_sum[0] << endl;
else
cout << seg.prod(x, y).sum[0] << endl;
}
}