经典数学问题
前言
本文是通过学习郑州大学ACM实验室课程总结的知识点,详细介绍了一些在算法题中可能会用到的一些基础的数学知识。
一、快速幂
**大致题目**
计算a的b次幂对m取模(mod运算)
b<=1e18
初步解题思想
如果说用需要算十进制的n次幂,就需要依次相乘进行计算。按照一般的思想,一秒内程序能运行一亿次运算即1e8次,但是b的范围的时间复杂度为O(b)b越大,时间复杂度越大,最后会超时。所以这时候就要将b转化成二进制进行运算了。
求出:
a^1
a^2
a^4
a^8
………
a^(2^n)
就有:
a^5[101(2)]=a^1*a^4
a^7[111(2)]=a^1*a^2*a^4
a^8[1000(2)]=a^8
这样算法复杂度为O(logb)……呀!大大降低了耶✌
代码实现:
#include<iostream>
using namespace std;//数据范围根据实际而定
long long quick_pow(int a, int b);
int mod;
int main()
{
int n, m;
cin >> n >> m>> mod ;
long long t = quick_pow(n, m);
cout << t << endl;
return 0;
}
long long quick_pow(int a, int b)
{
unsigned long long ret = 1;
while (b) {
if (b & 1)ret = ret * a % mod ;//b&1判断奇偶性,mod是需要取模的值
b >>= 1;//这里相当于b/=2(向左靠齐)
a = a * a % mod;
}
return ret;
}
特别注意:
a+b%m=(a%m+b%m)%m
a*b%m=(a%m*b%m)%m
a^b%m!=a%m^(b%m)
二、欧几里得算法(gcd(x,y))
题目: 求最大公约数
1.更相减损术(九章算术)
//循环
int gcd(int x,int y)
{
while(x!=y){
if(x>y) x=x-y;
else y=y-x;
}
return x;
}
//递归
int gcd(int x,int y)
{
if (x == y) return x;
else {
if (x > y) x = x - y;
else y = y - x;
return gcd(x, y);
}
}
时间复杂度: O(max(x,y))
1.辗转相除法(欧几里得算法)
int gcd(int x,int y)
{
if(!y)return x;
else return gcd(x%y,x);
}
时间复杂度: O(log max(x,y))
三.求取整的和
题目: 求解n/1+n/2+n/3+……+n/n(这里都是取整计算)
解题思路: 这里如果是一个一个结果然后相加数据大了就很容易超时(时间复杂度为O(n)),所以应该要进行找规律后再求和。
以5为例:
5/1 5/2 5/3 5/4 5/5
5 2 1 1 1
以10为例:
10/1 10/2 10/3 10/4 10/5 10/6 10/7 10/8 10/9 10/10
10 5 3 2 2 1 1 1 1 1
现在几乎可以看出基本规律了
每一个数开根号得到的数字之后都会有重复的结果出现,那么对于重复的结果就不用一个一个计算之后再相加了
这时候就需要解决重复区间的求解问题了
不难看出, 右端点的值都能被n整除,而左端点不一定
每一个左端点就是上一个区间的右端点加1就好了
直接看代码实现好了,容易理解一些
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n ;
int ans = 0;
for (int l = 1, r = 1; l <= n; l = r + 1) {
r = min(n, n / (n / l));//求右端点,也防止过界
ans += n / l * (r - l + 1);//求和
}
cout << ans;
return 0;
}
时间复杂度减为O(sqrt(n))
变式: 求n%1+n%2+n%3+……+n%n的结果
其实这里将n%t转化为n-n*n/t就好了。
总结
今天学习的主要是一些简单数学问题处理的时间优化问题,主要是一些数学思维、公式以及对数据的处理进行优化。