这周学习了线性代数,感觉很厉害的样子。。。
高斯消元
对于一组多项式方程(增广矩阵中,x[i, n+1]表示式子的值;x[i,j]表示第i个方程第j项的系数,在这里,增广矩阵可能不一定是n个,可能多可能少;opt表示运算规则):
(x[1,1]*a[1]) opt (x[1,2]*a[2]) opt … opt (x[1,n]*a[n])=x[1, n+1]
(x[2,1]*a[1]) opt (x[2,2]*a[2]) opt … opt (x[2,n]*a[n])=x[2, n+1]
…
(x[n,1]*a[1]) opt (x[n,2]*a[2]) opt … opt (x[n,n]*a[n])=x[n, n+1]
常见的opt有+和异或
消元:我们对于每个方程,假设为i,且x[i][now]不为0(now是当前被消元的变元,更一般的,只要能支持消掉后边方程的系数即可),然后对所有的j>i的方程,我们将所有x[j][now]不为0(或者需要消元的)用一种方式消掉。
回代:最后我们得到的是一个x[i][i]均不为0的方程(除非无解或此方程无意义)的倒三角矩阵。然后根据性质3将当前方程的其它j>i的a[j]和系数根据性质3转移到右式消去即可(此时a[j]一定是求出来的)
无解的情况:当存在一个矩阵使得左式=0而右式!=0那么无解。
自由变元:自由变元就是当这些未知量一旦确定,整个方程就确定了。但是这些量是未知的。(例如x+y=5,自由变元就是1,因为无论是x还是y确定,另一个就能唯一确定),而答案要求的是方案,那么显然因为自由变元是可以随便赋值的,而如果这些值只有2个,开和不开,那么方案数就是2^自由变元。
仅当n个不同的方程(就是无论怎么通过其它方程都不会将这两个方程变成一样)才能确定n个解。那么我们如果只确定了x个方程,那么自由变元的数量就是n-x。(这个x可以轻易得到,因为在高斯消元过程中,会将所有不同的方程消元,因为消元会将相同的方程消成这个样子:0=0。所以就能得到x了。
复杂度O(n^3)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define f(i,a,b) for(int i=a;i<=b;i++)
const double eps=0.000000001;
const int N=101;
double a[N][N+1];
int n;
int main(){
cin>>n;
f(i,1,n){
f(j,1,n+1) cin>>a[i][j];
}
f(i,1,n){
int top=i;
f(j,i,n) if(fabs(a[j][i]-a[top][i])<=eps) top=j;
f(j,1,n+1) swap(a[i][j],a[top][j]);
if(fabs(a[i][i])<=eps){
cout<<"No Solution"<<endl;
return 0;
}
f(j,i+1,n+1) a[i][j]/=a[i][i];
f(j,1,n)
if(i!=j)
f(k,i+1,n+1) a[j][k]-=a[j][i]*a[i][k];
}
f(i,1,n) printf("%.2lf\n",a[i][n+1]);
return 0;
}
矩阵快速幂
同正常快速幂
矩阵加速线性递推
例如求斐波那契数列,可以构造矩阵然后矩阵的幂就是答案
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
struct Matrix {
long long a[4][4];
}ans;
int mod = 1e9 + 7;;
Matrix Matrix_mul(Matrix A, Matrix B) {
Matrix C;
memset(C.a, 0 , sizeof(C.a));
for(int i = 1; i <= 3; ++i)
for(int j = 1; j <= 3; ++j)
for(int k = 1; k <= 3; ++k)
C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j] % mod) % mod;
return C;
}
Matrix qpow(Matrix A, int n) {
if(n <= 1) return A;
Matrix B = A; --n;
while(n) {
if(n & 1) B = Matrix_mul(B, A);
n >>= 1;
A = Matrix_mul(A, A);
}
return B;
}
int main() {
memset(ans.a, 0, sizeof(ans.a));
ans.a[1][1] = 1; ans.a[1][2] = 1; ans.a[1][3] = 0;
ans.a[2][1] = 0; ans.a[2][2] = 0; ans.a[2][3] = 1;
ans.a[3][1] = 1; ans.a[3][2] = 0; ans.a[3][3] = 0;
int T, x;
cin >> T;
while(T--) {
cin >> x;
Matrix A;
memset(A.a, 0, sizeof(A.a));
A = qpow(ans, x - 1);
cout << A.a[1][1] << endl;
}
return 0;
}
线性基
xor运算神器
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define REP(i, a, b) for(int i = a; i <= b; i++)
#define PER(i, a, b) for(int i = a; i >= b; i--)
#define LL long long
inline LL read(){
LL x = 0, flag = 1;char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') flag = - 1;
ch = getchar();
}
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x * flag;
}
template<typename T> struct linear_base {
static const int maxlog = 60;
T a[61];
void ins(T x) {
for(T i = maxlog; i >= 0; --i) {
if(x & ((T)(1) << i)) {
if(!a[i]) {
a[i] = x;
return;
} else x ^= a[i];
}
}
}
bool check(T x) {
for(T i = maxlog; i >= 0; --i)
if(x & ((T)(1) << i)) {
if(!a[i]) return 0;
else x ^=a[i];
}
return 1;
}
T qmax() {
T res = 0;
for(T i = maxlog; i >= 0; --i)
if((res ^ a[i]) > res) res ^= a[i];
return res;
}
T qmin() {
for(T i = 0; i <= maxlog; ++i)
if(a[i]) return a[i];
}
T query(int k) {
T tmp[61], res = 0, cnt = 0;
for(T i = 0; i <= maxlog; ++i) {
for(int j = i - 1; j >= 0; --j)
if(a[i] & ((T)(1) << j))
a[i] ^= a[j];
if(a[i]) tmp[cnt++] = a[i];
}
for(T i = 0; i < cnt; ++i)
if(k & ((T)(1) << i)) res ^= tmp[i];
return res;
}
};
linear_base<LL> s;
int main() {
int n = read();
REP(i, 1, n) s.ins(read());
printf("%lld\n", s.qmax());
return 0;
}
经典例题
给出一些运用矩阵运用线性代数的典型例题
UVa10870
Consider recurrent functions of the following form:
f(n) = a1f(n − 1) + a2f(n − 2) + a3f(n − 3) + . . . + adf(n − d), for n > d,
where a1, a2, . . . , ad are arbitrary constants.
A famous example is the Fibonacci sequence, defined as: f(1) = 1, f(2) = 1, f(n) = f(n − 1) +
f(n − 2). Here d = 2, a1 = 1, a2 = 1.
Every such function is completely described by specifying d (which is called the order of recurrence),
values of d coefficients: a1, a2, . . . , ad, and values of f(1), f(2), . . . , f(d). You’ll be given these numbers,
and two integers n and m. Your program’s job is to compute f(n) modulo m.
矩阵加速线性递推,构造相伴矩阵 F n = A ∗ F n − 1 F_n = A * F_{n-1} Fn=A∗Fn−1 A 为对角线为1最后一行为a的矩阵, F ( n ) = A n − d ∗ F ( d ) F(n)=A^{n-d}*F(d) F(n)=An−d∗F(d)
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 20;
typedef long long Matrix[maxn][maxn];
typedef long long Vector[maxn];
int sz, mod;
void matrix_mul(Matrix A, Matrix B, Matrix res) {
Matrix C;
memset(C, 0, sizeof(C));
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
for(int k = 0; k < sz; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
memcpy(res, C, sizeof(C));
}
void matrix_pow(Matrix A, int n, Matrix res) {
Matrix a, r;
memcpy(a, A, sizeof(a));
memset(r, 0, sizeof(r));
for(int i = 0; i < sz; i++) r[i][i] = 1;
while(n) {
if(n&1) matrix_mul(r, a, r);
n >>= 1;
matrix_mul(a, a, a);
}
memcpy(res, r, sizeof(r));
}
void transform(Vector d, Matrix A, Vector res) {
Vector r;
memset(r, 0, sizeof(r));
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
r[j] = (r[j] + d[i] * A[i][j]) % mod;
memcpy(res, r, sizeof(r));
}
int main() {
int d, n, m;
while(cin >> d >> n >> m && d) {
Matrix A;
Vector a, f;
for(int i = 0; i < d; i++) { cin >> a[i]; a[i] %= m; }
for(int i = d-1; i >= 0; i--) { cin >> f[i]; f[i] %= m; }
memset(A, 0, sizeof(A));
for(int i = 0; i < d; i++) A[i][0] = a[i];
for(int i = 1; i < d; i++) A[i-1][i] = 1;
sz = d;
mod = m;
matrix_pow(A, n-d, A);
transform(f, A, f);
cout << f[0] << endl;
}
return 0;
}
UVa1386
有一个细胞自动机,它是一个由 n 个元素组成的环,每个元素的值都必须是 \pmod {m}(modm) 意义下的。现在你需要对这个环进行 k 次操作,每次操作你需要把这个环内每个元素更新成与它距离不超过 d 的