高斯消元法求解线性方程组(
)
以上方程组的解有三种情况:无解、无穷多组解、唯一解。
利用高斯消元法后,左边呈完美阶梯型有唯一解,0(左边)=0(右边)有无穷多组解,0(左边)=b(非零)无解。
高斯消元法过程:枚举每一列c,找出这行绝对值最大的行,将这行换到最上面,将这行第一个数变成1,将该列下面所有行第c列消成0。所有列都操作完成后,在从下往上将每一行1后面的数消成0,最终最后一列的值就是唯一解。
java代码如下:
import java.util.*;
import java.io.*;
class Main{
static int N = 110;
static double[][] a = new double[N][N];
static double eps = 1e-6;
static int n;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
for(int i = 1;i <= n;i++){
String[] nums = br.readLine().split(" ");
for(int j = 1;j <= n+1;j++){
a[i][j] = Double.parseDouble(nums[j-1]); //字符串小数转换为数字
}
}
int res = gauss();
if(res == 0)
for(int i = 1;i <= n;i++)
System.out.printf("%.2fn",a[i][n+1]);
else if(res == 1) System.out.println("Infinite group solutions");
else System.out.println("No solution");
}
static int gauss(){
int row,col;
for(row = 1,col = 1;col <= n;col++){
int t = row;
for(int i = row;i <= n;i++){ //找出绝对值最大的行,从row开始是因为上面的操作好了
if(Math.abs(a[i][col]) > Math.abs(a[t][col])) t = i;
}
if(Math.abs(a[t][col]) < eps) continue; //这一列剩下的都是0
for(int i = col;i <= n+1;i++){ //将row行和t行交换,从col开始是因为前面的都是0了
double temp = a[row][i];
a[row][i] = a[t][i];
a[t][i] = temp;
}
//将第row行的col列数变成1;但是要从后往前更新,因为是根据第一个数更新的
for(int i = n+1;i >= col;i--) a[row][i] /= a[row][col];
//将第col列的row行下的数变成0;但是要从后往前更新,因为是根据第一个数更新的
for(int i = row+1;i <= n;i++){
if(Math.abs(a[i][col]) > eps){
for(int j = n+1;j >= col;j--){
a[i][j] = a[i][j] - a[i][col]*a[row][j];
}
}
}
row++; //不能与col同时增加,看上面的continue
}
if(row < n+1){ //不满秩:无解或者无穷多解
for(int i = row;i <= n;i++){
if(Math.abs(a[i][n+1]) > eps) return 2;
}
return 1;
}
for(int i = n;i >= 1;i--){ //将1后面的消成0
for(int j = i+1;j <= n;j++){
a[i][n+1] = a[i][n+1] - a[i][j]*a[j][n+1];
}
}
return 0;
}
}
高斯消元法求异或线性方程组
输入一个包含n个方程n个未知数的异或线性方程组。
方程组中的系数和常数为0或1,每个未知数的取值也为0或1。
求解这个方程组。
异或线性方程组示例如下:
思路:任意一个数跟它本身^都为0(用来把系数消掉)
java代码如下:
import java.util.*;
import java.io.*;
class Main{
static int N = 110;
static int[][] a = new int[N][N];
static int n;
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
for(int i = 1; i <=n;i++){
String[] nums = br.readLine().split(" ");
for(int j = 1;j <= n + 1;j++){
a[i][j] = Integer.parseInt(nums[j - 1]);
}
}
int res = gauss();
if(res == 0)
for(int i = 1;i <= n;i++){
System.out.println(a[i][n+1]);
}
else if(res == 1) System.out.println("Multiple sets of solutions");
else System.out.println("No solution");
}
static int gauss(){
int row,col;
for(row = 1,col = 1;col <= n;col++){
int t = row;
for(int i = row;i <= n;i++) {
if(a[i][col] != 0) t = i;
}
if(a[t][col] == 0) continue;
for(int i = col;i <= n+1;i++){
int temp = a[row][i];
a[row][i] = a[t][i];
a[t][i] = temp;
}
for(int i = row + 1;i <= n;i++){
if(a[i][col] != 0){
for(int j = n+1;j >= col;j--){
a[i][j] ^= a[row][j];
}
}
}
row++;
}
if(row < n+1){
for(int i = row;i <=n ;i++)
if(a[i][n+1] != 0) return 2;
return 1;
}
for(int i = n;i >= 1;i--){
for(int j = i + 1; j< n+1;j++){
a[i][n+1] ^= a[i][j]*a[j][n+1];
}
}
return 0;
}
}
组合数
解法1(组数10^5,
)
利用性质
(a个水果拿出b个苹果分成两种情况:一是拿出1个苹果,剩下a-1个拿出b-1个,二是拿出一个非水果,剩下a-1个拿出b个水果),预处理出2000以内所有的组合数(10^6)。
题目:AcWing885 求组合数 I
给定组询问,每组询问给定两个整数,请你输出的值。
java代码如下:
import java.util.*;
class Main{
static int N = 2010;
static long[][] c = new long[N][N];
static long MOD = (long)(1e9 + 7);
public static void main(String[] args){
Scanner s = new Scanner(System.in);
//预处理
for(int i = 0;i < N;i++)
for(int j = 0;j <= i;j++)
if(j == 0) c[i][j] = 1;
else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD;
int n = s.nextInt();
while(n-- > 0){
int a = s.nextInt();
int b = s.nextInt();
System.out.println(c[a][b]);
}
}
}
组合数
解法2(组数10^4,
)
利用这个公式
求解,先预处理出来
的阶乘
,然后逆元快速幂求出
的逆元。
(注意
)
(注意:0的阶乘是1,0的阶乘的逆元也是1;i的阶乘的逆元的计算公式与i的阶乘计算公式有点类似:infact[i]=infact[i-1]*getqmi(i,MOD-2,MOD))
题目:AcWing886 求组合数 II
给定组询问,每组询问给定两个整数,请你输出的值。
java代码如下:
import java.util.*;
class Main{
static int N = 100010;
static long[] fact = new long[N];
static long[] infact = new long[N];
static long MOD = (long)1e9 + 7;
static void init(){
fact[0] = 1;
infact[0] = 1; //0的阶乘的逆元也是1
for(int i = 1;i < N;i++) fact[i] = fact[i-1]*i % MOD;
for(int i = 1;i < N;i++) infact[i] = infact[i-1] * getqmi(i,MOD - 2,MOD) %MOD; //i阶乘的逆元求解公式
}
static long getqmi(int a,long b,long p){
long res = 1;
while(b != 0){
long x = b&1;
if(x == 1) res = res * a %MOD;
a = (int)((long)a * a % MOD);
b >>= 1;
}
return res;
}
public static void main(String[] args){
Scanner s = new Scanner(System.in);
init();
int n = s.nextInt();
while(n-- > 0){
int a = s.nextInt();
int b = s.nextInt();
long res = fact[a] * infact[a-b] % MOD * infact[b] % MOD;
System.out.println(res);
}
}
}
组合数
lucas(卢卡斯)定理:
时间复杂度:
证明:(AcWing 887. 求组合数 III(lucas定理))
题目:AcWing887 求组合数 III
给定组询问,每组询问给定三个整数,其中是质数,请你输出的值。
java代码如下:
import java.util.*;
class Main{
static long p; //防止溢出,全部用long表示(int 10^9级别 long 10^18级别)
static long a;
static long b;
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
while(n-- > 0){
a = s.nextLong();
b = s.nextLong();
p = s.nextInt();
long res = lucas(a,b);
System.out.println(res);
}
}
static long lucas(long a,long b){
if(a < p && b < p) return C(a,b);
else return C(a%p,b%p) * lucas(a/p,b/p) % p;
}
static long C(long a,long b){
long res = 1;
for(long i = a;i >= a-b+1;i--) res = res * i% p;
for(long i = 1;i <= b;i++) res = res * getqmi(i,p-2,p)%p;
return res;
}
static long getqmi(long a,long b ,long p){
long res = 1;
while(b != 0){
long x = b & 1;
if(x == 1) res = res*a%p;
a = a*a%p;
b >>= 1;
}
return res;
}
}
组合数
使用高精度乘法来将
的每一位求出:
分解质因数:
,
各个质数的次数求解:
(下取整,p是各个质数)
题目:AcWing888 求组合数 IV
输入,求的值。
注意结果可能很大,需要使用高精度计算。
java代码如下:
import java.util.*;
class Main{
static int N = 5010;
static int[] primes = new int[N];
static boolean[] state = new boolean[N];
static int a,b;
static int[] sum = new int[N]; //各个质数的个数;
static int count = 0;
public static void main(String[] args){
Scanner s = new Scanner(System.in);
a = s.nextInt();
b = s.nextInt();
getPrimes(a); //找出小于等于a的所有质数
for(int i = 0;i < count;i++){ //C^b_a = a!/(b!(a-b)!)
sum[i] = get(a,primes[i]) - get(b,primes[i]) - get(a-b,primes[i]);
}
List<Integer> list = new ArrayList<>();
list.add(1);
for(int i = 0;i < count;i++){ //将一个个list中的数与primes[i]的sum[i]次方相乘
for(int j = 0;j < sum[i];j++) //每循环一次乘以一个primes[i]
list = multiple(list,primes[i]);
}
for(int i = list.size()-1;i >= 0;i--) System.out.print(list.get(i));
}
static void getPrimes(int n){
for(int i = 2;i <= n;i++){
if(!state[i]) primes[count++] = i;
for(int j = 0;primes[j] <= n/i;j++){
state[i*primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
static int get(int a,int p){ //得到a!中质数p的次数[a/p]+[a/p^2]+[a/p^3]+...
int res = 0;
while(a != 0){
res += a/p;
a /= p;
}
return res;
}
static List<Integer> multiple(List<Integer> list,int p){
int t = 0;
List<Integer> res = new ArrayList<>();
for(int i = 0;i < list.size();i++){
t += list.get(i) * p;
res.add(t % 10);
t /= 10;
}
while(t != 0){
res.add(t % 10);
t /= 10;
}
return res;
}
}
卡特兰数
公式
题目:AcWing889 满足条件的01序列
给定个和个,它们将按照某种顺序排成长度为的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中的个数都不少于的个数的序列有多少个。
输出的答案对取模。
java代码如下:
import java.util.*;
class Main{
static int N = 100010;
static long[] fact = new long[2 * N];
static long[] infact = new long[N];
static long MOD = (long)(1e9 + 7);
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
init();
//1/(n+1)的模是乘以n+1的逆元,MOD是质数
long res = fact[2*n] * infact[n] % MOD * infact[n] %MOD * getqmi(n+1,MOD-2,MOD)%MOD;
System.out.println(res);
}
static void init(){
fact[0] = 1;
infact[0] = 1;
for(int i = 1;i < 2*N;i++) fact[i] = fact[i-1] * i % MOD;
for(int i = 1;i < N;i++) infact[i] = infact[i-1] * getqmi(i,MOD-2,MOD) % MOD;
}
static long getqmi(int a,long b,long p){
long res = 1;
while(b != 0){
long x = b & 1;
if(x == 1) res = res*a%MOD;
a = (int)((long)a * a %MOD);
b >>= 1;
}
return res;
}
}
注意:分式模某个质数的值是:分子的模乘以分母的逆元的模的成绩再模