20250714 线性代数

由于广大附集训第一天就讲线性代数我还啥都不会,所以今天做一下线性代数。


XOR线性基(板

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int maxbit = 60;

int p[maxbit];

void insert(int x){
    for(int i = maxbit-1;i >= 0;i--){
        if(!(x >> i & 1)){
            continue;
        }
        if(!p[i]){
            p[i] = x;
            return;
        }
        x ^= p[i];
    }
}

int query(){
    int ans = 0;
    for(int i = maxbit - 1;i >= 0;i--){
        if((ans ^ p[i]) > ans){
            ans ^= p[i];
        }
    }
    return ans;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i = 0;i < n;i++){
        int x;
        cin>>x;
        insert(x);
    }
    cout<<query()<<endl;
    return 0;
}


[TJOI2008] 彩灯

其实也是板子,因为灯的操作相当于XOR

有因为一组基底能表示不同的结果,所以答案就是2的幂


P4151 [WC2011] 最大XOR和路径

典题,先求出来随便的路径上的异或和,然后用环来增光他,把所有环一次dfs插入线性基即可。


最开始的这条链,其实它可以随便选。我们考虑以下这种情况:

假设路径A比路径B优秀一些,而我们最开始选择了路径B。显然,A与B共同构成了一个环。如果我们发现路径A要优秀一些,那么我们用B异或上这个大环,就会得到我们想要的A!

所以这道题的算法是:找出所有环,扔进线性基,随便找一条链,以它作为初值求最大异或和就可以了。


高斯消元(板

点击查看代码
#include<bits/stdc++.h>
using namespace std;
constexpr  double EPS = 1e-8;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n;
    cin>>n;

    vector<vector<double>> matrix(n,vector<double>(n + 1));

    for(int i = 0;i < n;i++){
        for(int j = 0;j < n+1;j++){
            cin>>matrix[i][j];
        }
    }

    for(int c = 0;c < n;c++){
        int pivot_row = c;
        for(int r = c + 1;r < n;r++){
            if(abs(matrix[r][c]) > abs(matrix[pivot_row][c])){
                pivot_row = r;
            }
        }
        if(pivot_row != c){
            swap(matrix[c],matrix[pivot_row]);
        }
        if(abs(matrix[c][c]) < EPS){
            cout<<"No Solution"<<endl;
            return 0;
        }
        double pivot_val = matrix[c][c];
        for(int j = c;j < n + 1;j++){
            matrix[c][j] /= pivot_val;
        }

        for(int r = 0;r < n;r++){
            if(r != c){
                double factor = matrix[r][c];
                for(int j = c;j < n+1;j++){
                    matrix[r][j] -= factor * matrix[c][j];
                }
            }
        }
    }
    cout<<fixed<<setprecision(2);
    for(int i = 0;i < n;i++){
        if(abs(matrix[i][n]) < EPS){
            cout<<0.00<<endl;
        }
        else{
            cout<<matrix[i][n]<<endl;
        }
    }
}



P4035球形空间产生器

只需求出一个点(x1​,x2​,x3​……xn​),使得:
∑j=0n​ (ai,j​−xj​)2=C
其中C为常数,ai,j​是点的坐标。
改方程组由n+1个n元二次方程构成,当然不是线性的,怎么办呢??
我们可以通过相邻两个方程做差,变成n个n元一次方程组,消去常数C
有点像数学中数列求通项公式或者前n项和
于是有:
∑j=1n​ (ai,j2​−ai+1,j2​−2xj​(ai,j​−ai+1,j​))=0
把变量放左边,常数放右边:
∑j=1n​ 2(ai,j​−ai+1,j​)xj​ = ∑j=1n​(ai,j2​−ai+1,j2​) (i=1,2,3……n)


[HEOI2015] 小 Z 的房间

前置:

矩阵树定理

拉普拉斯矩阵

设无向图有 n 个节点。拉普拉斯矩阵 L 是一个 n×n 的矩阵。定义如下:

Li,i​ 的值为节点 i 的度数,即有多少条边和节点 i 相连。
Li,j​ (i=j) 的值为节点 i 和节点 j 之间相连的边数的相反数。

将拉普拉斯矩阵去掉任意的一行和一列,得到的矩阵求行列式,即是原图的生成树数量。

整除消元

高斯校园如果模数不是质数并且会弄出来小数的话,你取模是肯定会爆炸的。

所以就需要整除消元,使用辗转相除法,倍数相等,多一个logV


```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long 
const ll MOD = 1e9;

ll determinant(vector<vector<ll>> mat, int n) {
    ll res = 1;
    int sign = 1;
    for(int i = 0; i < n; ++i) {
        int pivot = i;
        for(int j = i; j < n; ++j)
            if(mat[j][i]) { pivot = j; break; }
        if(mat[pivot][i] == 0) return 0;
        if(pivot != i) {
            swap(mat[i], mat[pivot]);
            sign *= -1;
        }
        for(int j = i + 1; j < n; ++j) {
            while(mat[j][i]) {
                ll t = mat[i][i] / mat[j][i];
                for(int k = i; k < n; ++k) {
                    mat[i][k] = (mat[i][k] - mat[j][k] * t % MOD + MOD) % MOD;
                    swap(mat[i][k], mat[j][k]);
                }
                sign *= -1;
            }
        }
    }
    for(int i = 0; i < n; ++i)
        res = res * mat[i][i] % MOD;
    if(sign == -1) res = (MOD - res) % MOD;
    return res;
}

int n, m, cnt;
char room[10][10];
int id[10][10];

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m;
    cnt = 0;
    for(int i = 0; i < n; ++i) {
        cin >> room[i];
        for(int j = 0; j < m; ++j)
            id[i][j] = (room[i][j] == '.') ? cnt++ : -1;
    }
    vector<vector<ll>> mat(cnt, vector<ll>(cnt, 0));
    int dx[] = {1, 0}, dy[] = {0, 1};
    for(int i = 0; i < n; ++i)
    for(int j = 0; j < m; ++j) {
        if(id[i][j] == -1) continue;
        for(int d = 0; d < 2; ++d) {
            int ni = i + dx[d], nj = j + dy[d];
            if(ni >= n || nj >= m || id[ni][nj] == -1) continue;
            mat[id[i][j]][id[i][j]]++;
            mat[id[ni][nj]][id[ni][nj]]++;
            mat[id[i][j]][id[ni][nj]]--;
            mat[id[ni][nj]][id[i][j]]--;
        }
    }
    vector<vector<ll>> mat2(cnt-1, vector<ll>(cnt-1));
    for(int i = 1; i < cnt; ++i)
    for(int j = 1; j < cnt; ++j)
        mat2[i-1][j-1] = mat[i][j];
    int ans = determinant(mat2, cnt-1);
    cout << (ans<0?ans + MOD:ans) << endl;
    return 0;
}

cpp
posted @ 2025-07-14 09:10  Dreamers_Seve  阅读(8)  评论(0)    收藏  举报