线性代数模板笔记
线性代数
高斯消元
模意义下,乘法继续,把除法改为乘逆元即可
double a[maxn][maxn], x[maxn];
//x先存b,再存x
int equ, var;//方程组与未知数个数
int Gauss(){
int i, j, k, col, max_r;
for(k=0, col=0; k<equ && col<var; k++, col++){
max_r=k;
for(i=k+1; i<equ; i++)
if(fabs(a[i][col]>fabs(a[max_r][col])))
max_r=i;
if(fabs(a[max_r][col])<eps) return 0;
if(k!=max_r){
for(j=col; j<var; j++)
swap(a[k][j], a[max_r][j]);
swap(x[k], x[max_r]);
}
//当前列最大值换到与列相等行(有唯一解情况)
x[k]/=a[k][col];
for(j=col+1; j<var; j++) a[k][j]/=a[k][col];
a[k][col]=1;
for(i=0; i<equ; i++)
if(i!=k){
x[i]-=x[k]*a[i][col];
for(j=col+1; j<var; j++)
a[i][j]-=a[k][j]*a[i][col];
a[i][col]=0;
}
}
return 1;
}
equ=var=n;
不知道为什么要把最大数换到\(a_{ii}\),可能除大的对精度比较友好吧。
更改一些顺序,就合并了好多代码,计算次数稍微增多,但代码极短,下面的写法更棒了,以下var=n*2,是可以求矩阵的逆的:
ll a[maxn][maxn*2];
ll x[maxn];
int n, var;//var=2n,表示行列
bool Gauss() {
for(int i=0, r; i<n; i++) {
r=i;
for(int j=i+1; j<n; j++)
if(a[j][i]>a[r][i]) r=j;
if(r!=i) swap(a[i], a[r]);//换的是一行
if(!a[i][i]) return false;//行列式为0,不存在逆
ll inv=qpm(a[i][i], M-2);//=/a[i][i]
for(int k=0; k<n; k++) {//枚举k行
if(k==i) continue;
ll t=a[k][i]*inv%M;//t=aki/aii
x[k]=((x[k]-t*x[i])%M+M)%M;
for(int j=i; j<var; j++){//枚举k行的每j列
a[k][j]=(a[k][j]-t*a[i][j]%M+M)%M;
}
}
x[i]=x[i]*inv%M;//i行i列要变成1
for(int j=0; j<var; j++) a[i][j]=a[i][j]*inv%M; //一定要后变化
}
return true;
}
行列式求解
竟然可long long
ll determinant(int n){
ll res=1;
for(int i=1; i<=n; i++){
if(!L[i][i]){
bool flag=0;
for(int j=i+1; j<=n; j++){
if(L[j][i]){
flag=1;
for(int k=i; k<n; k++){
swap(L[i][k], L[j][k]);
}
res=-res;
break;
}
}
if(!flag) return 0;
}
for(int j=i+1; j<=n; j++){
while(L[j][i]){
ll t=L[i][i]/L[j][i];
//存在有余,类似辗转相除
//因为求模时不能出现小数
for(int k=i; k<=n; k++){
L[i][k]-=L[j][k]*t;
swap(L[i][k], L[j][k]);
}
res=-res;
}
}
res*=L[i][i];
}
return res;
}
//double版(oi_wiki)determinant
double gauss() {
double ans = 1;
for (int i = 0; i < n; i++) {
int sid = -1;
for (int j = i; j < n; j++)
if (abs(mat[j][i]) > eps) {
sid = j;
break;
}
if (sid == -1) continue;
if (sid != i) {
for (int j = 0; j < n; j++) {
swap(mat[sid][j], mat[i][j]);
ans = -ans;
}
}
for (int j = i + 1; j < n; j++) {
double ratio = mat[j][i] / mat[i][i];
for (int k = 0; k < n; k++) {
mat[j][k] -= mat[i][k] * ratio;
}
}
}
for (int i = 0; i < n; i++) ans *= mat[i][i];
return abs(ans);
}
带M
模M版
只需在t完成除法后即可正常模数。
//普通行列式
ll determinant(int n){
ll res=1;
for(int i=1; i<=n; i++){
// if(!L[i][i]){ //J题,加上wa#0??不知道什么意思
// bool flag=0;
// for(int j=i+1; j<=n; j++){
// if(L[j][i]){
// flag=1;
// for(int k=i; k<n; k++){
// swap(L[i][k], L[j][k]);
// }
// res=-res;
// break;
// }
// }
// if(!flag) return 0;
// }
for(int j=i+1; j<=n; j++){
while(L[j][i]){
ll t=L[i][i]/L[j][i];
for(int k=i; k<=n; k++){
L[i][k]=(L[i][k]-(t%M*L[j][k])%M+M)%M;
swap(L[i][k], L[j][k]);
}
res=(-res%M+M)%M;
}
}
res=(res*L[i][i])%M;
}
return res;
}
//laplace/kirchhoff矩阵
//不用求逆元
ll determinant(int n){
ll res=1;
for(int i=1; i<=n; i++){
if(!L[i][i]){
bool flag=0;
for(int j=i+1; j<=n; j++){
if(L[j][i]){
flag=1;
for(int k=i; k<n; k++){
swap(L[i][k], L[j][k]);
}
res=-res;
break;
}
}
if(!flag) return 0;//不加这个不会wa,可能tle,L题
}
for(int j=i+1; j<=n; j++){
while(L[j][i]){
ll t=L[i][i]/L[j][i];
for(int k=i; k<=n; k++){
L[i][k]=(L[i][k]-(t%M*L[j][k])%M+M)%M;
swap(L[i][k], L[j][k]);
}
res=(-res%M+M)%M;
}
}
res=(res*L[i][i])%M;
}
return (res+M)%M;//区分此处
}
求矩阵的逆
更改一些顺序,就合并了好多代码,计算次数稍微增多,但代码极短,下面的写法更棒了,以下var=n*2,是可以求矩阵的逆的:
ll a[maxn][maxn*2];
ll x[maxn];
int n, var;//var=2n,表示行列
bool Gauss() {
for(int i=0, r; i<n; i++) {
r=i;
for(int j=i+1; j<n; j++)
if(a[j][i]>a[r][i]) r=j;
if(r!=i) swap(a[i], a[r]);//换的是一行
if(!a[i][i]) return false;//行列式为0,不存在逆
ll inv=qpm(a[i][i], M-2);//=/a[i][i]
for(int k=0; k<n; k++) {//枚举k行
if(k==i) continue;
ll t=a[k][i]*inv%M;//t=aki/aii
x[k]=((x[k]-t*x[i])%M+M)%M;
for(int j=i; j<var; j++){//枚举k行的每j列
a[k][j]=(a[k][j]-t*a[i][j]%M+M)%M;
}
}
x[i]=x[i]*inv%M;//i行i列要变成1
for(int j=0; j<var; j++) a[i][j]=a[i][j]*inv%M; //一定要后变化
}
return true;
}
线性基
希望用集合\(D\)内元素,相互异或后可得到另一更大集合\(S\),现求最小的集合\(D\)可使得\(S\)不变。
p[0]至p[w]全为1时,一定可以表示所有\(2^1-1=1\)至\(2^w-1\)。
ll p[maxn], v[maxn];//p为辅助,v为D中元素
void ins(ll x){
ll px=x;
for(int i=62; i>=0; i--){
if(!(x>>i)) continue;
if(p[i]){
x^=p[i];
continue;
}
p[i]=x;
v[i]=px;
break;
}
}
bool vis[maxm*2];
int k=0;
void dfs(ll x){//如果存在一个排列per使相邻两项xor在D中,这样输出排列即可
vis[x]=1;
printf("%lld ", x);
for(int i=0; i<k; i++){
if(vis[x^v[i]]) continue;
dfs(x^v[i]);
}
}
ll d[maxm];
//http://codeforces.com/problemset/problem/1163/E
int main() {//求最大长度全排列(0~2^k-1)使相邻两项xor在D中
int n;
cin>>n;
for(int i=0; i<n; i++) scanf("%lld", &d[i]);
sort(d, d+n);
for(int i=0; i<n; i++) ins(d[i]);
for(int i=0; i<=62; i++){//p中间有0一定不能构造该位为1
if(p[i]==0) break;
k++;
}
while(k && v[k-1]>=(1<<k)) k--;//该位最小来自D中更大数,则小于该位的数异或不能在D中(使k--break,而不是k++)
if(k==0){
printf("0\n0");
return 0;
}
printf("%d\n", k);
dfs(0);
return 0;
}

浙公网安备 33010602011771号