学完就忘,重学。不过说实话,对于校内上的那种[“线性代数”]^(100以内四则运算)忘不忘有意义吗。
和标题一样,就只有一点,而且组织混乱。
略过部分
下面所有的内容都是基于向量空间内的意义。
行列式
行列式评价了一个线性变换对空间的放缩程度。对于一个二维空间,它就评价了对有向面积的放缩比例,三维则是体积。
所以对于行列式为0的变换,依照其意义,可以认为将空间进行了“降维”(压扁,但是它仍然在那个维度上)。那么这也是能够使用行列式判断一个向量组是否满秩(张成的空间和向量维数一致)的直观表现。
这同时也是可以使用行列式计算三维空间下的体积的直观表现,如果你把平行六面体理解成是单位立方体的变换。
∣ M 1 M 2 ∣ = ∣ M 1 ∣ ∣ M 2 ∣ |M_1M_2|=|M_1||M_2|
∣ M 1 M 2 ∣ = ∣ M 1 ∣ ∣ M 2 ∣
基
基是向量空间中的概念,指的是一个可以张成向量空间V V V 的线性无关向量组B \mathfrak {B} B 。而对于一般情况下的有限向量空间,求解其基的方法就是从向量空间内依次删除线性相关项,直到其变为线性无关组。
其满足以下性质
V V V 是B \mathfrak {B} B 的极小生成集,B \mathfrak {B} B 的任何子集都无法张成V V V 。
B \mathfrak {B} B 是V V V 的极大线性无关组。
线性基
当计算为异或时,可以将整数以二进制分解作为向量,在这种意义下对这样的一个整数集合求基称为线性基。
其求法就像上文所说的求法,依次的考虑某一个整数是否限性相关。在实际编写时通过高斯消元来完成。
其具体思路为维护一个对角矩阵。由高位开始使用已有元素对新加入的元素进行消元,若完全消除,则线性相关;若未完全消除(由高位计算到第i位时无法消除),则将其计入基中,并使用所有剩余的低位对该元素对应位置继续消元,再使用该元素向上对先前的所有元素第i位进行消元。如此的复杂度O ( n lg n ) O(n\lg n) O ( n lg n ) 。
由这个过程,我们能够得到的信息包含但不限于
基:一组由原整数组组成的基,由消元过程判断。
线性基:一组由原整数张成空间中的标准基。
一向量空间的所有基的长度相同,同时称为空间的维数。对于一个由n维的向量构成的向量空间,其维数不大于n。
Problem: Exclusive OR
Given n n n integers A 1 , ⋯ , A n A_1,\cdots , A_n A 1 , ⋯ , A n . For all 1 ≤ i ≤ n 1\leq i \leq n 1 ≤ i ≤ n , determine the maximum value by caculating xor of i i i numbers that are repeatly chosen from A A A .
1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ A i < 2 18 1\leq n \leq 2 \times 10^5, 0 \leq A_i < 2^{18}
1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ A i < 2 1 8
利用异或卷积,令a[i]表示数i是否出现过,那么
b k = ∑ i ∑ j a i a j [ i xor j = k ] b_k=\sum_i\sum_j a_ia_j[i~\text{xor}~j =k]
b k = i ∑ j ∑ a i a j [ i xor j = k ]
即异或和为k k k 的方案数。把a a a 自己卷自己,统计每次卷完有方案的最大的k k k 。
对于集合A,其张成的空间维数不可能超过18,则至多选择18个向量就可以构成其基,至多有18个向量就可以取到最大的xor。而从构成最大的向量开始,接下来就只是选择一个元素加入(所以至少算到19)和抵消,即每隔1个答案相同了。
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <bitset> #include <tuple> using namespace std;using ll = long long ;const int XN = 650000 ;const int MOD = 998244353 ; ll INV2 = 0 ;ll qpow (ll x, ll a) { ll res = 1 ; for (; a; a >>= 1 , x = (ll)x * x % MOD) { if (a & 1 )res = (ll)res * x % MOD; } return res % MOD; }template <class T>void FWT (ll a[], int n, T F) { for (int len = 2 ; len <= n; len *= 2 ) for (int i = 0 ; i < n; i += len) for (int j = i; j < i + len / 2 ; ++j) F (a[j], a[j + len / 2 ]); }void Txor (ll& x, ll& y) { auto a = (x + y) % MOD; auto b = (x - y + MOD) % MOD; x = a, y = b; }void Ixor (ll& x, ll& y) { auto a = (x + y) * INV2 % MOD; auto b = (x - y) * INV2 % MOD; x = a, y = b; }int ans[XN]; ll a[XN], b[XN];int main () { ios::sync_with_stdio (false ); INV2 = qpow (2 , MOD - 2 ); int n; cin >> n; int maxx = 0 ; for (int i = 1 ; i <= n; i++) { int x; cin >> x; maxx = max (x, maxx); a[x] = 1 ; b[x] = 1 ; } int t = 1 ; while (t <= maxx)t *= 2 ; ans[1 ] = maxx; FWT (b, t, Txor); for (int i = 2 ; i <= 20 ; i++) { FWT (a, t, Txor); for (int i = 0 ; i <= t; i++) { a[i] = (ll)a[i] * b[i] % MOD; } FWT (a, t, Ixor); for (int j = t; j >= 0 ; j--) { if (a[j]) { ans[i] = j; break ; } } } for (int i = 21 ; i <= n; i++)ans[i] = ans[i - 2 ]; for (int i = 1 ; i <= n; i++)cout << ans[i] << " " ; cout << endl; return 0 ; }
线性变换
这里主要考虑通过矩阵对向量空间进行线性变换。考虑的办法主要是基于对基的操作。通过将某一个变换拆解为单一动作的矩阵,我们能够方便的去玩空间。
例如,计算三维空间中的某个点P按一条线A A A 为轴旋转γ \gamma γ 度后的位置。一个粗暴的思路是先将x x x 轴转到A A A 上去,计算P P P 于原位置在新的基下的坐标P ′ P' P ′ ,对P ′ P' P ′ 以x x x 轴旋转θ \theta θ 度,对此时的P ′ P' P ′ 重新逆回原基下的坐标。
以三维直线在x − y x-y x − y 平面上的投影和x x x 轴正半轴的夹角为θ \theta θ ,以直线与z z z 轴正半轴的夹角为ϕ \phi ϕ ,那么首先以θ \theta θ 旋转,再以ϕ \phi ϕ 旋转即可把x x x 轴转到A A A 上去。两次旋转的变换矩阵分别为
以z轴旋转。
M z = [ cos θ − sin θ 0 sin θ cos θ 0 0 0 1 ] M_z=\begin{bmatrix}
\cos \theta & -\sin \theta & 0 \cr
\sin \theta & \cos \theta & 0 \cr
0 & 0 & 1
\end{bmatrix}
M z = ⎣ ⎡ cos θ sin θ 0 − sin θ cos θ 0 0 0 1 ⎦ ⎤
以y轴旋转
M y = [ sin ϕ 0 − cos ϕ 0 1 0 cos ϕ 0 sin ϕ ] M_y=\begin{bmatrix}
\sin \phi & 0 & -\cos \phi \cr
0 & 1 & 0 \cr
\cos \phi & 0 & \sin \phi
\end{bmatrix}
M y = ⎣ ⎡ sin ϕ 0 cos ϕ 0 1 0 − cos ϕ 0 sin ϕ ⎦ ⎤
在此时,对于由Ω \Omega Ω 以M M M 变换到的Ω ′ \Omega' Ω ′ ,在Ω ′ \Omega' Ω ′ 空间下的坐标左乘变换M M M 就能得到其在Ω \Omega Ω 的坐标。M M M 即为Ω ′ \Omega' Ω ′ 和Ω \Omega Ω 之间的桥梁。
那么,考虑如何将二者复合。首先执行z轴旋转,将x x x 转到A A A 在x − y x-y x − y 平面上的投影位置,得到空间Ω ′ \Omega' Ω ′ 。而后,我们需要对在Ω \Omega Ω 下Ω ′ \Omega' Ω ′ 的基在Ω ′ \Omega' Ω ′ 下的坐标再次执行y y y 轴旋转将其最终转到A A A 上去,称为空间Ω 1 \Omega_1 Ω 1 ,再把Ω ′ \Omega' Ω ′ 下的Ω 1 \Omega_1 Ω 1 的基换算回Ω \Omega Ω 里,准备进行下一步的转换。这个过程即
M z M y ( M z − 1 M z I ) = M z M y M_zM_y(M_z^{-1}M_zI)=M_zM_y
M z M y ( M z − 1 M z I ) = M z M y
然后计算P P P 位于Ω 1 \Omega_1 Ω 1 下的坐标,即求解M P ′ = P ⟹ P ′ = M − 1 P MP'=P \implies P'=M^{-1}P M P ′ = P ⟹ P ′ = M − 1 P ,计算∣ M ∣ |M| ∣ M ∣ 恒非0 0 0 ,则P ′ P' P ′ 总可求出。而后对P ′ P' P ′ 执行以x x x 轴(即A)旋转题目给定的γ \gamma γ 角。再次计算M P ′ MP' M P ′ 求出P P P 即为答案。
以x x x 轴旋转
M x = [ 1 0 0 0 cos γ sin γ 0 − sin γ cos γ ] M_x=\begin{bmatrix}
1 & 0 & 0 \cr
0 & \cos \gamma & \sin \gamma \cr
0 & -\sin \gamma & \cos \gamma
\end{bmatrix}
M x = ⎣ ⎡ 1 0 0 0 cos γ − sin γ 0 sin γ cos γ ⎦ ⎤
这几个矩阵简单画一下图就可以推出来。逆矩阵使用高斯消元处理伴随矩阵即可。
博客在允许 JavaScript 运行的环境下浏览效果更佳