acwing算法基础课笔记4(三)

高斯消元

求解线性方程组,其实就是线代求解,思想是利用增广矩阵来求

 高斯消元的步骤(线代hh):

#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;

const int N = 110;
const double eps = 1e-6;

int n;
double a[N][N];

int gauss()
{
	int c, r;
	for(c=0,r=0;c<n;c++)
	{
		int t = r;
		for(int i=r;i<n;i++)//从第r行开始,前面r行已经确定 
		{
			if(fabs(a[i][c]) > fabs(a[t][c])) t = i;
		}
	    if(fabs(a[t][c]) < eps) continue;
	    
	    for(int i=c;i<=n;i++) swap(a[t][i], a[r][i]);// 从第c列开始交换,前面c列都已经是0了,从0也可以
	    for(int j=n;j>=c;j--) a[r][j] /= a[r][c];//要从后开始化1 
	    for(int i=r+1;i<n;i++)
	    {
	    	if(fabs(a[i][c]) > eps)
	    	{
	    		for(int j=n;j>=c;j--)
	    		{
	    			a[i][j] += ( (-1) * a[i][c] * a[r][j]);//每一个数都要减去当前这行的第c列乘以已经确定的r行的当前列j列 
				}
			}
		}
		r++;
	}
	
	if(r< n)
	{
		for(int i=r;i<n;i++)
		{
			if(fabs(a[i][n]) > eps)//0 = !0,所以无解 
			return 2;
		}
		return 1;
	}
	
	for(int i=n-1;i>=0;i--)
	{
		for(int j=i+1;j<n;j++)
		{
			a[i][n] += ((-1) * a[i][j] * a[j][n]);//每一行的b都要减去这一行中非0的值乘以对应列的b 
		}
	}
	return 0;
}

int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n+1;j++)
		{
			cin>>a[i][j];
		}
	}
	
	int t = gauss();
	if(t == 0)
	{
		for(int i=0;i<n;i++) printf("%.2lf\n", a[i][n]);
	}
	else if(t == 1) puts("Infinite group solutions");
	else puts("No solution");
	return 0;
}

求组合数

根据问题不同来判定使用哪种方法来解组合数问题

1、给定n 组询问,每组询问给定两个整数 a,b,求出 C_{a}^{b} mod (1e9+7) 的值。

n最大10万,a、b小于2000;

代码思路:递推(复杂度400万左右)

 代码:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2010, mod = 1e9 + 7;

int c[N][N];

int init()
{
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<=i;j++)
		{
			if(!j) c[i][j] = 1;
			else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;
		}
	}
}

int main()
{
	int n;
	cin>>n;
	init();
	
	while(n--)
	{
		int a, b;
		cin>>a>>b;
		cout<<c[a][b]<<endl;
	}
	return 0;
}

2、问题与1相同,但是n最大为1万,a、b小于10万

代码思路:预处理出阶层,逆元的阶层,然后用快速幂求逆元 

 

由费马定理得b的p-1次方与1同余P,所以b的乘法逆元就是b的p-2次方,当p为质数时才成立。 

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010, mod = 1e9 + 7;
typedef long long LL;

int fact[N], infact[N];

int qmi(int a, int k, int p)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res = (LL) res * a % p;
		a = (LL) a * a % p;
		k >>= 1;
	}
	return res;
}

int main()
{
	fact[0] = infact[0] = 1;
	for(int i=1;i<N;i++)
	{
		fact[i] = (LL) fact[i-1] * i % mod; 
		infact[i] = (LL) infact[i-1] * qmi(i, mod - 2, mod) % mod;
	}
	
	int n;
	cin>>n;
	while(n--)
	{
		int a, b;
		cin>>a>>b;
		cout<<(LL) fact[a] * infact[b] % mod * infact[a-b] % mod<<endl;
	}
	return 0;
}

 3、20组询问,a,b为10的18次方,p小于10的5次方

思路:卢卡斯定理(画线行)

代码: 

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

int p;

int qmi(int a, int k)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res = (LL) res * a % p;
		k >>= 1;
		a = (LL) a * a % p;
	}
	return res;
}

int C(int a, int b)
{
	int res = 1;
	for(int i=1, j=a;i<=b;i++, j--)
	{
		res = (LL) res * j % p;
		res = (LL) res * qmi(i, p-2) % p;
	}
	return res;
}

LL lucas(LL a, LL b)
{
	if(a < p && b < p) return C(a, b);
	return (LL) C(a % p, b % p) * lucas(a / p, b / p) % p;
}

int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		LL a, b;
		cin>>a>>b>>p;
		cout<<lucas(a, b)<<endl;
	}
	return 0;
}

 4、求组合数4

a,b小于5000,但是求的就是 C_{a}^{b},没有模,数可能很大,需要用高精度来求

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int N = 5010;
 
int primes[N], cnt;
int sum[N];
bool st[N];

int get_primes(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i]) primes[cnt++] = i;
		for(int j=0;primes[j]<=n/i;j++)
		{
			st[primes[j] * i] = true;
			if(i % primes[j] == 0) break;
		}
	}
}

int get(int n, int p)
{
	int res = 0;
	while(n)
	{
		res += n/p;
		n /= p;
	}
	return res;
}

vector<int> mul(vector<int> a, int b)
{
	vector<int> c;
	int t = 0;
	for(int i=0;i<a.size();i++)
	{
		t += a[i] * b;
		c.push_back(t % 10);
		t /= 10;
	}
	while(t)
	{
		c.push_back(t % 10);
		t /= 10;
	}
	return c;
}

int main()
{
	int a, b;
	cin>>a>>b;
	get_primes(a);
	
	for(int i=0;i<cnt;i++)
	{
		int p = primes[i];
		sum[i] = get(a, p) - get(b, p) - get(a - b, p);
	}
	
	vector<int> res;
	res.push_back(1);
	for(int i=0;i<cnt;i++)
	{
		for(int j=0;j<sum[i];j++)
		{
			res = mul(res, primes[i]);
		}
	}
	for(int i=res.size()-1;i>=0;i--) cout<<res[i];
	cout<<endl;
	return 0;
}

5.卡特兰数

 

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 1e5 + 10, mod = 1e9 + 7;

int qmi(int a, int k, int p)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res =(LL) res * a % mod;
		k >>= 1;
		a = (LL) a * a % mod;
	}
	return res;
} 

int main()
{
	int n;
	cin>>n;
	int a = 2 * n, b = n;
	int res = 1;
	for(int i=a;i>a-b;i--) res = (LL) res * i % mod;
	for(int i=1;i<=b;i++) res = (LL) res * qmi(i, mod - 2, mod) % mod;
	
	res = (LL) res * qmi(n + 1, mod - 2, mod) % mod;
	cout<<res<<endl;
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值