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
浙公网安备 33010602011771号