然而这东西在OI里好像并没有什么用
这上面的题不多,洛谷里连标签都没有
1.排列和逆序对
1.1排列
将n个数按任意顺序排序,得到长度为n的排列
显然有\(n!\)种不同排列
在一个排列种,对换其中两数,其他数不动,的得到另一个排列,叫做对换
1.2逆序对
对于排列\(a_1,a_2,\dots a_n\),\((i,j),i<j,\text{且}a_i>q_j\)称为一个逆序对
好了可以认为以上都是废话
1.3
奇排列:逆序对数量是奇数
偶排列:逆序对数量是偶数
一些定理:
- 对换改变排列的奇偶性
设对换\(a_i,a_j(i\leq j)\)两个元素
考虑相邻的两个元素对换,逆序对数量\(+1\text{或}-1\),改变排序奇偶性
我们对换\(a_i,a_j\)时每次都只对换相邻的两个元素
\(a_i,a_{i+1},\dots,a_j\)
\(\rightarrow a_{i+1},a_i,a_{i+2},\dots ,a_j\)
\(\rightarrow a_{i+1},a_{i+2},a_i,a_{i+3},\dots,a_j\)
\(\dots\)
\(\rightarrow a_{i+1},a_{i+2},\dots a_j,a_i\)
然后再用相同的方法把\(a_j\)向左移动
移动\(i\)时移动\(j-i\)次,移动\(j\)要\(j-i-1\)次
所以共移动\(2(j-i)-1\)次,奇数
改变奇数次奇偶性,所以奇偶性肯定被改变 - 在\(n>1\)的排列中,奇偶排列各占一半
尝试将奇偶排列一一对应
设\(a_1,a_2,\dots,a_n\)为奇排列
则\(a_2,a_1,a_3,\dots,a_n\)为偶排列
所以建立起这种一一对应的关系,就可以证明奇偶排列数量相等 任意排列可以经给一系列对换变成自然排列,所作对换次数的奇偶性与这个排列的奇偶性相同
构造一种对换顺序,将1换到\(a_1\)上,2交换到\(a_2\)上,\(\dots\)
对换次数那个就不证了严格怎么证不大会
然而这些定理行列式种用到也不多2.行列式
2.1定义
终于进入正题
n阶行列式由\(n^2\)个数通过下式确定一个数
\[
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \\
a_{2,1}&a_{2,2}&\dots&a_{2,n} \\
\dots&\dots&\dots&\dots\\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \\
\end{array}\right|
\]
\[=\sum_{j_1j_2\dots j_n}{sgn(j_1j_2\dots j_n)}a_{1,j_1}a_{2,j_2}\dots a_{n,j_n}\]
当然看到这个式子很是懵
叙述一遍:枚举\(1\)到\(n\)的全排列(\(j_1,j_2,\dots,j_n\)),对于每一种排列,求\(a_{1,j_1}a_{2,j_2}\dots a_{n,j_n}\)再乘: $sgn(j_1j_2\dots j_n)=
\begin{cases}
1&j_1j_2\dots j_n\text{是偶排列}\
-1&j_1j_2\dots j_n\text{是奇排列}\
\end{cases}
$然后还是很懵
其实就是每行取一个数,每列取一个,乘起来,再乘那个\(sgn\)
可以自己找个例子试试
要注意矩阵和行列式是不同的,行列式就是一个数,矩阵是数表
显然,按照定义直接求行列式的值是\(O(n!n)\)
2.2一堆定理
行列互换,值不变
比如
\[ \left |\begin{array}{cccc} 1&2&3 \\ 4&5&6 \\ 7&8&9 \\ \end{array}\right| \]
变成:
\[ \left |\begin{array}{cccc} 1&4&7 \\ 2&5&8 \\ 3&6&9 \\ \end{array}\right| \]
在取数的时候,\(\sum\)中的每一项都是只在每行取一个数,每列取一个
所以无论从行还是列去看,都是取了所有数
然后这个有一种“对称的”感觉感性理解用一个数去乘某个行列式等于用这个数乘此行列式的某一行
比如这个数是\(k\)乘在第\(i\)行里
则:
\[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
在那个\(\sum\)中的每一项肯定都会乘一个第\(i\)行的数,所以把这个第\(i\)行的数中的\(k\)提出了,就是\(k\times \text{这个行列式乘k以前的值}\)- 如果行列式中某一行是两组数之和,则这个行列式等于分别以这两组数为改行,其余行与这个行列式相等的两个行列式之和
即:
$$
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
b_{i,1}+c{i,1}&b_{i,2}+c{i,2}&\dots&b_{i,n}+c{i,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
=
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
b_{i,1}&b_{i,2}&\dots&b_{i,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
- \left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
c_{i,1}&c_{i,2}&\dots&c_{i,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
$$
在等号左边的行列式等于\(\sum{\dots\times (b_{i,j}+c_{i,j})}\)简写了
把那个\(\dots\)乘进去,就是\(\sum{\dots\times b_{i,j}}\)和\(\sum{\dots\times c_{i,j}}\),分别对应等号右边的两个行列式
交换行列式两行,行列式符号改变
设把\(i,j\)两行交换
则改变的只有\(sgn(j_1j_2\dots j_n)\),根据我们在排列中的定理,做一次对换奇偶性改变,则它的正负就改变,所以变号如果行列式中两行成比例,行列式等于0
考虑下面这个行列式:
\[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
首先由定理2,把它变成:
\[k\times \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
再由定理4,交换这里相同的两行,行列式形式不变,值就不变,但它的值又应该变为相反数
相反数等于本身,所以必然是0- 把一行的某个倍数加到另外一行,值不变
把第\(i\)行的\(k\)倍加到第\(j\)行:
$$
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
a_{i,1}&a_{i,2}&\dots&a_{i,n} \
\dots&\dots&\dots&\dots\
ka_{i,1}+a_{j,1}&ka_{i,2}+a_{j,2}&\dots&ka_{i,n}+a_{j,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
=
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
a_{i,1}&a_{i,2}&\dots&a_{i,n} \
\dots&\dots&\dots&\dots\
a_{j,1}&a_{j,2}&\dots&a_{j,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
- \left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
a_{i,1}&a_{i,2}&\dots&a_{i,n} \
\dots&\dots&\dots&\dots\
ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
\[ \]
=
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&\dots&a_{1,n} \
\dots&\dots&\dots&\dots\
a_{i,1}&a_{i,2}&\dots&a_{i,n} \
\dots&\dots&\dots&\dots\
a_{j,1}&a_{j,2}&\dots&a_{j,n} \
\dots&\dots&\dots&\dots\
a_{n,1}&a_{n,2}&\dots&a_{n,n} \
\end{array}\right|
$$
用到了定理5和定理3
这也是后面要常用的一个定理
终于没了
然后又因为定理1,下面几个定理对行的操作也都可以到列上
3如何更快的求值
3.1考虑一种特殊行列式
\[
\left |\begin{array}{cccc}
a_{1,1}&a_{1,2}&a_{1,3}&\dots&a_{1,n} \\
0&a_{2,2}&a_{2,3}&\dots&a_{2,n} \\
0&0&a{3,3}&\dots&a{3,n}\\
\vdots&\vdots&\vdots&\ddots&\vdots \\
0&0&0&\dots&a_{n,n} \\
\end{array}\right|
\]
显然它的值是\(\prod_{i=1}^{n}a_{i,i}\),因为求行列式的那个\(\sum\)里其他项都会乘个0
3.2高斯消元
这个东西还可以用来解n元一次方程
有了这个,就可以进入高斯消元了
考虑把一个普通行列式变成上面那种形式
一列一列的消
考虑第一列,对于第\(i\)行(当然\(i\)不等于1),让它每个元素\(a_{i,j}\)减去\(\dfrac{a_{i,1}}{a_{1,1}}\times a_{1,j}\)(定理6)
这样就让\(2\)到\(n\)行的第一列的元素变成了0
然后用第2行的\(a_{2,2}\)到\(a_{2,n}\)这\(n-1\)个元素,将\(a_{3,2}\)到\(a_{n,2}\)(也就是第二列)消成0
同理,用第3行消第3列,第4行消第4列,\(\dots\)
当然不用考虑在消后面的元素的时候把前面已经消成0的破坏
因为如果在用第\(i\)行消第\(i\)列,\(a_{i,1}\)到\(a_{i,i-1}\)和他们之间的每一个数肯定都已经消成了0,所以用他们的任意倍去减下面的\(a_{j,h}(1\leq h\leq i-1\),它已经被消成0了)还是0
所以可以写出代码
因为没有找到题,本文所有代码未验证,可能有错
double gauss(){ double x=1; for(reg int i=1;i<=n;i++){ for(reg int j=i;j<=n;j++){//找一个不为0的项 if(std::abs(a[j][i])>1e-8) { if(j==i) break; std::swap(a[i],a[j]); x=-x;//根据定理5,交换两行行列式变好 break; } } if(std::fabs(a[i][i])<=1e-8) return 0;//没有不为0的项了,行列式值为0 for(reg int j=i+1;j<=n;j++){ double k=a[j][i]/a[i][i]; for(reg int h=1;h<=n;h++) a[j][h]-=a[i][h]*k; } } for(reg int i=1;i<=n;i++) x*=a[i][i]; return x; }
然而,这样可能精度会出问题
比如我们被给定的行列式都由正数组成,然而我们一直在用实数来算
所以我们基本不会用上面那种。。。
3.3精度更高的高斯消元
可以发现,整个高斯消元中,产生最大精度误差的是这一句:
double k=a[j][i]/a[i][i];
考虑如何减小
比如我们由两个数,\(a=10^5,b=10^{-5}\)
则\(\dfrac{k}{10^5}\)和\(\dfrac{k}{10^{-5}}\)谁精度高呢?这里\(k\)是c++浮点数
当然是第一种,因为\(\dfrac{k}{10^{-5}}=k\times 10^5\),所以,更多的数位会去用来表示整数部分,表示小数部分的少了,就没有第一种误差小
所以我们每次可以选一个最大的a[i][i]
来做除数
具体实现在上一个代码改一改就好了
因为在数学上一个矩阵或是什么中的最大数叫主元,所以整个方法叫主元高斯消元
double gauss(){ double x=1; for(reg int i=1;i<=n;i++){ int maxline=i;//最大数的行号 for(reg int j=i+1;j<=n;j++) if(std::abs(a[maxline][i])<std::fabs(a[j][i])) maxline=j; if(maxline!=i) std::swap(a[maxline],a[i]),x=-x; for(reg int j=i+1;j<=n;j++){ double k=a[j][i]/a[i][i]; for(reg int h=1;h<=n;h++) a[j][h]-=k*a[i][h]; } } for(reg int i=1;i<=n;i++) x*=a[i][i]; return x; }
3.4辗转相消法
又是然而,主元高斯消元减少了误差,但没完全避免
所以我们能不能在过程中不用实数运算来避免误差呢?
用一种类似于辗转相除的方法
当然也要用定理6
还是一列一列消元,不断用数大的行减去数小的行,直到减成0
然后可以像求\(\gcd\)的时候一样把减改成模
然后这种方法的复杂度是\(O(n^2(n+\log n))\)
因为求\(n\)个数的\(\gcd\)的最大公因数实际上是\(O(n+\log n)\)的
直接看代码吧,我实在想不出怎么描述了.....
很像辗转相除的
但是它没有用double
的运算,所以它的常数和前面两种差别不大
int gauss(){ int x=1; for(reg int i=1;i<=n;i++){ for(reg int j=i+1;j<=n;j++){ while(a[j][i]!=0){ int k=a[i][i]/a[j][i]; for(reg int h=1;h<=n;h++) a[i][h]-=a[j][h]*k; for(reg int h=1;h<=n;h++) std::swap(a[i][h],a[j][h]); x=-x; } } } for(reg int i=1;i<=n;i++) x*=a[i][i]; return x; }
3.5玄学方法
比如说我取一个极大的模数
行列式中每个数对它求逆元
然后除法的时候直接乘逆元
但是这种方法局限太大,模数要大于运算过程和结果的所有数(让他又能求逆元,又能不被取模后的结果影响)
一般题目中对结果和运算过程数没有约定,不好去取模数。还是别用了。。。
4.题目
代填
5.OI以外的简单应用
代填
打个笔记latex快累死我了.....
来源:https://www.cnblogs.com/suxxsfe/p/12446673.html