文章目录
专题复习九(9.10):数学相关
1 最大公约数与最小公倍数
最大公约数的代码:
int gcd(int a, int b){
if(b == 0) return a;
else return gcd(b, a%b);
}
lcm(a,b) = a / gcd(a,b) * b; //最小公倍数
2 分数的表示
除法需要额外注意,如果读入的除数是0,需要直接特判输出。
struct Fraction{
int up, down;
};
Fraction reduction(Fraction result){
if(result.down < 0){
result.up = -result.up;
result.down = -result.down;
}
if(result.up == 0){
result.down = 1;
}
else{
int d = gcd(abs(result.up), abs(result.down));
result.up /= d;
result.down /= d;
}
return result;
}
3 素数
3.1 判断素数
bool isPrime(int n){
if(n <= 1) return false;
int sqr = (int)sqrt(1.0* n);
for(int i=2; i<=sqr; i++){
if(n % 2 == 0) return false;
}
return true;
}
3.2 筛法获取素数表
const int MAXN = 101; //表长
int prime[MAXN], pNum = 0; //prime存放所有素数,pNum存放素数个数
bool notPrime[MAXN] = {0}; //初始假设都是素数
void Find_Prime(){
for(int i=2; i<MAXN; i++){
if(notPrime[i] == false){
prime[pNum++] = i;
for(int j = i+i; j<MAXN; j += i){
notPrime[j] = true;
}
}
}
}
4 质因子分解
由于1本身不是素数,因此它没有质因子。由于每个质因子可以不止出现一次,可以定义结构体factor,用来存放质因子及其个数。
质因子分解的思路:
- 枚举1~sqrt(n)范围内的所有质因子p,判断p是否是n的因子。如果是就给fac数组增加质因子p,并初始化其个数为0。然后,只要p还是n的因子,就让n不断除以p,每次操作令p的个数加一;如果不是直接跳过。
- 如果以上步骤后n仍然大于1,说明有一个大于sqrt(n)的质因子(有可能是n本身),这时只需要把剩下的n加入fac数组,并设置其个数为1。
struct factor{
int x, cnt;
}fac[10];
int num = 0;
int sqr = sqrt(1.0 * n);
for(int i=0; i<pNum && prime[i] <= sqr; i++){
if(n % prime[i] == 0){
fac[num].x = prime[i];
fac[num].cnt = 0;
while(n % prime[i] == 0){
n /= prime[i];
fac[num].cnt++;
}
num++;
}
if(n == 1) break;
}
if(n != 1){
fac[num].x = n;
fac[num++].cnt = 1;
}
如果要求一个正整数N的因子个数,只需要对其进行质因子分解,得到各质因子的pi分别为e1,e2,e3…ek,则N的因子个数就是(e1 + 1)*(e2 + 1)…
5 大整数运算
高精度A + B
struct bign{
int d[1000];
int len;
bign(){
fill(d, d+1000, 0);
len = 0;
}
};
bign change(string str){
bign a;
a.len = str.size();
for(int i=0; i<a.len; i++){
a.d[i] = str[a.len - i - 1] - '0';
}
return a;
}
bign Add(bign a, bign b){
int carry = 0;
bign c;
for(int i=0; i<a.len || i<b.len; i++){
int temp = a.d[i] + b.d[i] + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
if(carry != 0){
c.d[c.len++] = carry;
}
return c;
}
void printBign(bign a){
for(int i=a.len-1; i>=0; i--){
printf("%d",a.d[i]);
}
}
高精度A - B,先比较A和B的大小,如果A比B小,就交换A和B,计算出结果后加上一个负号即可。
int compare(bign a, bign b){
if(a.len > b.len) return 1;
else if(a.len < b.len) return -1;
else{
for(int i=a.len-1; i>=0; i--){
if(a.d[i] > b.d[i]) return 1;
else if(a.d[i] < b.d[i]) return -1;
}
return 0;
}
}
bign Sub(bign a, bign b){
bign c;
for(int i=0; i<a.len || i<b.len; i++){
if(a.d[i] < b.d[i]){
a.d[i+1]--;
a.d[i] += 10;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while(c.len >= 2 && c.d[c.len - 1] == 0){
c.len--;
}
return c;
}
高精度A × 低精度B:
bign multi(bign a, int b){
bign c;
int carry = 0;
for(int i=0; i<a.len; i++){
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
while(carry != 0){
c.d[c.len++] = carry % 10;
carry = carry / 10;
}
return c;
}
高精度A ÷ 低精度B:
bign divide(bign a, int b, int &r){
bign c;
c.len = a.len;
for(int i=a.len-1; i>=0; i--){
r = r*10 + a.d[i];
if(r < b) c.d[i] = 0;
else{
c.d[i] = r / b;
r = r % b;
}
}
while(c.len >= 2 && c.d[c.len - 1] == 0){
c.len--;
}
return c;
}
6 组合数
6.1 求n!中有多少个质因子p
O(logN)的算法:cal(n,5)即为n!末尾0的个数。
int cal(int n, int p){
int ans = 0;
while(n){
ans += n / p;
n /= p;
}
return ans;
}
6.2 组合数的计算
递归计算,复杂度O(n2);
long long res[67][67] = {0};
long long C(long long n, long long m){
if(m == 0 || n == m) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = C(n-1, m) + C(n-1, m-1);
}
定义变形计算,复杂度O(m):
long long C(long long n, long long m){
long long ans = 1;
for(long long i=1; i<=m; i++){
ans = ans * (n - m + i) / i;
}
return ans;
}
计算C(n,m)%p,假设两倍的p不会超过int,可以支持m<=n<=1000, p<=10的9次方
int res[1010][1010] = {0};
int C(int n, int m){
if(m == 0 || n == m) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = (C(n-1, m) + C(n-1, m-1)) % p;
}
如果要支持long long级别的m,n,用lucas算法
int Lucas(int n, int m){
if(m == 0) return 1;
return C(n%p, m%p) * Lucas(n/p, m/p) % p;
}