guss 消元&线性基学习笔记
guass 消元
高斯消元是一种解决多元一次线性方程组的通解,时间复杂度是 \(O(n^3)\) 的。
比较常规的思路就是把一个方程矩阵通过上下相加减的模式变换成一个倒三角的矩阵,然后从下往上消元,这样可以变换成 \(a_ix_i = b_i\) 的形式,即可得到解。
当然这样的形式会有无解的情况,比如 \(a_i=0\) 的情况会是无解的情况。\(b_i=0\and a_i\neq 0\) 的情况是多解的情况。
实践起来是不难的:
void guass(){
for(int i=0; i<n; i++) {
int m=i;
for(int j=i+1; j<n; j++)
if(fabs(a[j][i])>fabs(a[m][i]))m=j;
if(m!=i)swap(a[i],a[m]);
if(fabs(a[i][i])<eps)
return 0;
for(int j=0; j<n; j++) {
if(j!=i) {
double d=a[j][i]/a[i][i];
for(int k=i+1; k<=n; k++)
a[j][k]-=a[i][k]*d;
}
}
}
for(int i=0; i<n; i++) {
if(abs(a[i][i])<=eps) return 0;
else x[i]=a[i][n]/a[i][i];
}
return 1;
}
其实这种方法多在期望 dp 中遇到(因为期望 dp 的转移方程比较史)当然有时可以使用 \(O(n)\) 的马尔可夫变换来写,当然不是这里的内容。
实际应用的时候就是把方程列下来,进行消元即可。
线性基
这个词是取自于线性代数中的,意思是用最少的基底可以用线性的计算来构成所给的元素,当然这个线性的运算是你定义的,可以是加减也可以是异或。
如何求异或的线性基呢?这里先给出代码:
void insert(int val){
for(int i=60;i>=0;i--){
if(val&1ll<<i){
if(f[i])val^=f[i];
else return f[i]=val,void();
}
}
}
这里只讨论对 \(val\) 的二进制下 \(x\) 位是 1 的情况。
- 若这一位还未有线性基,直接把这一位的线性基设为 \(val\)。
- 若已经有了线性基,异或上这个线性基,继续往下做。
第一步是好理解的,而第二步正确性是毋庸置疑的,保证了这一位的线性基的最高位是不是其他位,保证了后面操作的正确性。
接着说查询操作
查询异或最大值:
线性基从大到小依次枚举,若异或上这个线性基更大则直接异或上即可,这也体现了上面插入操作的重要性。
查询最小值:
只需要求出最小的线性基即可,容易证明,最小的线性基异或上其他的线性基都不会更优。
查询k小值:
首先处理这个序列的线性基 \(p\),对于每一个 \(p_i\),枚举 \(j=1 \sim i\),如果 \(p_i\) 二进制的第 \(j\) 位为 1,那么 \(p_i\) 异或上 \(p_{j-1}\)。
将 \(k\) 先转成二进制,假如第 \(i\) 位为 1,则 \(ans\) 异或线性基中第 \(i\) 个元素(注意不是直接异或 \(p_{i-1}\))。
void work() {
for(int i=1; i<=60; i++)
for(int j=1; j<=i; j++)
if(f[i]&(1ll<<j-1))f[i]^=f[j-1];
}
int kth(int x) {
if(cnt<n)x--;
if(x>=(1ll<<cnt))return -1;
if(x==0)return 0;
int res=0;
for(int i=0; i<=60; i++) {
if(f[i]) {
if(x&1)res^=f[i];
x>>=1;
}
}
return res;
}
查询这个值是否存在:
直接插一下这个值,若这个值能插进去就是不存在,否则存在。

浙公网安备 33010602011771号