20240627 数学知识一 40:32
-
类似质数 试除法
869 求约数
只要枚举到n/i就够了!!!!
时间复杂度 O根号n big o notation
排序52:00
lnlnn比根号n小 忽略不计 了
数论题关键算时间复杂度,每一步都算才不会超时!!!
cpp
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> get_divisors(int n ){
vector<int> res;
// 约数 只用看到n/i就够了!!!
for(int i = 1; i <= n/i; i ++){
if(n %i == 0)
{
res.push_back(i);
// 边界处理,防止重复放 n/i =i
if(i != n/i) res.push_back(n/i);
}
}
sort(res.begin(), res.end());
return res;
}
int main(){
int n;
cin >> n;
while( n --){
int x;
cin >> x;
auto res= get_divisors(x);
for(auto t :res) cout << t<<" ";
puts("");
}
return 0;
}
记两个不同的公式
870 约数个数
基于算术基本定理
显然:因为排列组合,从α1个p1里选不同个数的p1有0-p1共1+p1种选法
所以约数个数是(α1+1)(α2+1) ......
概率论讲过
N的约数d可以写成:0<=β1<=α1
先分解质因数把a1 a2。。分别分解
map:存<指数的底数pi,质数的幂ai>,然后约数个数就是(幂a1+1)(幂a2+1)(幂+1)= Π(幂+1)
cpp
#include<iostream>
#include<algorithm>
#include<unordered_map> //hash表快一点 不map
using namespace std;
typedef long long LL;
const int mod = 1e9+7; //答案需对 109+7取模。
int main(){
int n;
cin >> n;
unordered_map<int, int> primes;
while( n--) {
int x;
cin >> x;
// 枚举约数到x/i
for(int i = 2; i <= x/i; i ++){
while(x % i == 0){
x /= i;
primes[i] ++; //以这个质因数为底的幂 +1
}
}
if( x> 1) primes[x] ++; //x是一个比较大的质因数 为啥
}
LL res = 1;
for(auto prime: primes) res= res*(prime.second +1) % mod;
cout << res;
return 0;
}
871 约数之和
类似上面,只不过把约束和表示出来,然后暴力展开
乘积总个数=上面约数个数
如何求t=p^0+...p^a 不需要用快速幂再加速了(有logα算法分治)
总和 求p^0+ ... + p^a;做a次的p*t +1妙啊
Auto+map好用!!
cpp
#include<iostream>
#include<algorithm>
#include<unordered_map> //hash表快一点 不map
using namespace std;
typedef long long LL;
const int mod = 1e9+7; //答案需对 109+7取模。
int main(){
int n;
cin >> n;
unordered_map<int, int> primes;
while( n--) {
int x;
cin >> x;
// 枚举约数到x/i
for(int i = 2; i <= x/i; i ++){
while(x % i == 0){
x /= i;
primes[i] ++; //以这个质因数为底的幂 +1
}
}
if( x> 1) primes[x] ++; //x是一个比较大的质因数 为啥
}
LL res = 1;
// 870比只改了这里
for(auto prime: primes)
{
int p = prime.first, a= prime.second;//p底,a指数
LL t= 1; //总和 求p^0+ ... + p^a;做a次的p*t +1妙啊
while(a --) t= (t*p+1) %mod;
res = res * t % mod;
}
cout << res;
return 0;
}
最大公约数------欧几里得算法------辗转相除法 01:19:23
时间复杂度Logn不用更相减损术
性质:
-
如果d能整除a(就是a % d =0)而且d整除b,那么d能整除a,b线性组合ax+by
-
(a,b)=(b, a % b) a和b的最大公约数= b和a模b 的最大公约数
因为a % b= a- (a|b) *b (a模b= a - (a整除b)*b= a -c * b)
所以证明 (a,b) = (b, a-c * b) c=a|b
设a,b公约数是d
那么左边得 d|a且d|b,又因为性质一所以 d| ax +by 这里x=1, y=-c
所以b和a-c*b公约数是d
反过来右边推出 d|b且d|a-cb
所以d|bx+(a-c b)y,此时y=1,x=c,能推出d|a
所以a b公约数也是d
所以左右所有的公约数集合都相同,所以左边的最大公约数也是右边的 证毕
模板:
cpp
#include<iostream>
using namespace std;
// 很常用
int gcd(int a, int b){
// 如果b=0,返回(b, a%b)的公约数
// 否则b=0, (a,0)=返回a 不能是0
return b ? gcd(b, a%b): a;
}
int main(){
int n;
scanf("%d", &n);
while(n --){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", gcd(a, b));
}
return 0;
}