1.数论 【每一步都要想时间复杂度,看能不能做】
2.组合计数
3.高斯消元
4.简单博弈论
给定 n 个正整数 ai,判定每个数是否是质数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出格式
共 n 行,其中第 i 行输出第 i 个正整数 ai 是否为质数,是则输出 Yes,否则输出 No。
数据范围
1≤n≤100,
1≤ai≤231-1
输入样例:
2
2
6
输出样例:
Yes
No
质数定义:在大于1的整数中,如果只包含1和本身这两个约数,就被看成质数 , 反之为合数
(1)质数的判定—试除法 O(n1/2) - - - O(sqrt(n))
质数性质:;[d * n / d == n] --> d | n 【'|'表示整除】, 则 n/d | n 枚举成对出现的小的部分的数,即可推全部: d <= n/d – > d2 <= n 【优化原理】
(2)分解质因数—试除法 O(n1/2)
优化:n中最多只包含一个大于 n1/2 ,【证明:质因子相乘不会大于n,反证出最多一个】
筛去质数的倍数
埃及的一个数学家发明的:埃氏筛法
一个质数定理: 1 ~ n中有 n / logn 个质数 n(1 + 1/2 + 1/4 + 1/8 + 1/16 …)调和级数 O(nloglogn) ~= O(n)
线性筛法
- i % pj【primes[j] 】== 0 ,primes[j] 一定是i的最小质因子
2.i % pj != 0 , pj一定小于i的所有质因子,pj也一定是 pj * i 的最小质因子
对于一个合数x,假设pj是x的最小质因子,当i枚举到x / pj的时候
每个数只被筛选过一次
【最佳选用线性筛法】
1、题目:
给定一个正整数n,请你求出1~n中质数的个数。
输入格式
共一行,包含整数n。
输出格式
共一行,包含一个整数,表示1~n中质数的个数。
数据范围
1≤n≤106
输入样例:
8
输出样例:
4
2、引用~基本思想:
(1)朴素筛法:不管是合数还是质数,筛掉2~n中每个数的倍数 O(nlogn);
(2)埃氏筛法:因为每个合数都是由有限个质数相乘得到的,仅筛掉质数的倍数 O(nloglogn);
(3)用线性筛法快(建立质数表):在埃式筛法中,有的合数会被不同的质数筛多次,会浪费时间,
而每个合数的最小质数是唯一的(否则就不是最小质数),所以只需要用每个合数的最小的质数将它筛掉一次 即可O(n)。
#include
using namespace std;
bool is_prime(int n)
{
if(n < 2)return false; //先排除非法值
for(int i= 2;i <= n / i ; i++) //[防止溢出写法]
if(n % i == 0)
return false;
return true; //如果false会直接返回,所以可以不加else
}
#include
using namespace std;
const int N = 10000010;
int primes[N] ,cnt;
bool st[N];
bool get_prims(int n)
{
for(int i = 0;i <= n;i++)
{
if(!st[i]) //st质数表
{
primes[cnt ++] = i;
for(int j = i + i;j <= n;j += i) st[j] = true;
}
}
}
同样n只会被最小质因子筛掉 【提速原理:不会重复筛选质数的倍数】
简记:每找到一个质数 , 筛质数表中存放的质数,必筛到 i 的最小质因子
#include
using namespace std;
const int N = 10000010;
int primes[N] ,cnt;
bool st[N];//true筛去 ,剩下未被标记的为质数
void get_prims(int n) //【找质数-不是判断质数,建立质数表】
{
for(int i = 2;i <= n;i++)
{ //因为i从2开始找质数 2,3,5都是质数,且4是2的倍数,在2判合数时已经被标记,会跳过
if(!st[i]) primes[cnt++] = i;//primes质数表cnt统计质数个数
for(int j = 0;primes[j] <= n / i;j ++) //遍历到 n/(自身= i) 即可
{
st[primes[j] * i] = true; //st为true筛去合数 ,剩下false为质数
if(i % primes[j] == 0)break;//成立时 , primes[j] 一定是i的最小质因子 (不成立时,primes[j]一定小于j的最小质因子,则也包含i的最小质因子)
}
}
}
/*
复杂度介于 O(logn) ~ O(n1/2)
简记:质因数^cnt^ 的乘积
联系约数个数:就是unorder_map primes 存放[ 质因数,个数(幂)]
void divide(int n) //分解结果一定是质数
{
for(int i = 2;i <= n / i;i++)
if(n % i == 0) //枚举到i,说明 n是i的倍数 && 已经除去了2~~i-1质因子,即n,i也不包含任何2 ~~ i-1之间的质因子,[i就一定是个质数],只能被自己和1整除
{
int s = 0;
while(n % i == 0)
{
n /= i;
s++;
}
printf("%d %d\n",i,s); //存放:primes[cnt++] = i ,sum[cnt++] = s;
}
if(n > 1) printf("%d %d\n",n,1); //最多可能存在一个 > sqrt(n) 的质因数 [此处优化后,i只要枚举到n/i]
puts("");
}
题目描述 :
给定 n 个正整数 ai,对于每个整数 ai,请你按照从小到大的顺序输出它的所有约数。
输入输出格式 :
输入
第一行包含整数 n。
接下来 n 行,每行包含一个整数 ai。
输出
输出共 n 行,其中第 i 行输出第 i 个整数 ai 的所有约数。
输入输出样例 :
输入
2
6
8
输出
1 2 3 6
1 2 4 8
思路:
①枚举 1 ~ sqrt(n) ,判断是否x % i == 0
②因为约数都是成对出现【 d * ( n/d ) == n --> d | n ,n/d | n [‘|’:整除],只枚举到小的那个约数就可以】
若是x % i != i,我们再将x % i也储存到结果数组
③对res进行sort排序algorithm
简记: 开vector< int > res
res.push_back(i) : 约数存放 i与 n/i ( i != n / i)不要重复存放
sort(res.begin(),res.end());
main:for(auto t : res) printf("%d ",res[t]) ; puts(""); 【c++11】 迭代器遍历换成for 新型
或 for(int t = 0;t < res.size();t++) printf("%d ",res[t]) ; puts("");
#include
#include
#include
#include
using namespace std;
vector<int> get_divisors(int n)//试除法
{
vector<int> res;
for(int i = 1;i <= n / i ;i++) //枚举到sqrt(n) ,这样写是为了体现成对出现的约数 可以改成i * i <= n
if(n % i == 0) //是约数 ,同时 n/i 也是约数
{
res.push_back(i); //放入
if(i != n / i) res.push_back(n / i); // 平方相等时,i 与 n/i 两个约数一样,不要重复放
}
sort(res.begin(),res.end());
return res; //数组首地址
}
int main() {
int n;
cin >> n;
while(n --)
{
int x;
cin >> x;
vector<int> res = get_divisors(x);
for(auto t : res)printf("%d" , t );
puts("");
/*法二 :【vector数组下标循环(非迭代器)】
for(int t = 0;t < res.size();t++)
{
cout << res[t] << " ";
}
puts("");
*/
}
return 0;
}
给定n个正整数ai,请你输出这些数的乘积的约数个数,答案对109+7取模。
输入格式
第一行包含整数n。
接下来n行,每行包含一个整数ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对109+7取模。
数据范围
1≤n≤100,
1≤ai≤2*109
3
2
6
8
输出样例:
12
解题思路:约数个数等于各个质因子的幂指数+1 的乘积,然后利用这个定理,先把各个数分解质因子,用hash表存储幂指数,最后乘起来即可。
分解定理:每种数的分解选法对应一种约数个数
1 ~ n的约数个数共有nlogn , 平均每个数logn个
(所有指数+1 )再相乘 = 约数个数
简记:
联系约数个数:就是unorder_map primes 对应关系 [ 质因数下标key, 个数(幂)]
【unorder_map 存储 {key, mapped} 对的哈希表 】
#include
using namespace std;
#include
#include
const int mod = 1e9 + 7;
typedef long long ll;
int main()
{
int n;
cin >> n;
unordered_map <int,int> primes;
while(n --)
{
int x;
cin >> x; //累加每个a[i]的质因子个数
for(int i = 2;i <= x / i;i++)
while(x % i == 0)
{
x /= i;
primes[i] ++;//存了所以质因数的指数
}
if(x > 1) primes[x] ++;
}
ll res = 1; //乘积初始值: 1
for(auto prime : primes) res = res * (prime.second + 1) % mod; //仅此不同
cout << res << endl;
return 0;
}
给定 n 个正整数 ai,请你输出这些数的乘积的约数之和,答案对 109+7 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数之和,答案需对 109+7 取模。
数据范围
1≤n≤100,
1≤ai≤2×109
输入样例:
3
2
6
8
输出样例:
252
用算术基本定理分解质因数:
unordered_map一个用来存P,一个用来存对应的α;
记住约数之和公式:( P10 + P11 + … + P1α1 ) × … × ( Pk0 + Pk1 + … + Pk^α k^ ) 【证明:展开式相等】
乘积的个数 (α1 + 1)(α2 + 2)* … *(αk + k) == ( P10 + P11 + … + P1α1 ) , a1 * a2 * … *an == p1α1 *…*pkαk
其中的每一小项用秦九韶算法来计算:while (a – ) t = (t * p + 1) % mod;
简记:【与约数个数算法思路一样:算出约数,个数次幂】 乘积初始值 res :1
main:unordered_map primes; {key,次幂}
i <= x / i :满足约数 :primes[i] ++;
判断是否有大于sqrt(n)的约数 :if(n>1)
公式:∑ 约数^(幂+1)^
for(int j;primes[j] != 0;j++) res = res * (primes[j].second + 1) % mod;
#include
using namespace std;
#include
#include
const int mod = 1e9 + 7;
typedef long long ll;
int main()
{
int n;
cin >> n;
unordered_map <int,int> primes;
while(n --)
{
int x;
cin >> x; //累加每个a[i]的质因子个数
for(int i = 2;i <= x / i;i++)
while(x % i == 0)
{
x /= i;
primes[i] ++;//存了所以质因数的指数
}
if(x > 1) primes[x] ++;
}
//约数之和
ll res = 1; //乘积初始值: 1
for(auto prime: primes)
{
int p = prime.first , a = prime.second;
ll t = 1;
while(a--) t = (t * p + 1) % mod; //pi^0 + pi^1 + ... + pi^a1
res = res * t % mod;
}
//for(auto prime: primes) res = res * (prime.second + 1) % mod; 约数个数
cout << res << endl;
return 0;
}
给定n对正整数ai,bi,请你求出每对数的最大公约数。
输入格式
第一行包含整数n。
接下来n行,每行包含一个整数对ai,bi。
输出格式
输出共n行,每行输出一个整数对的最大公约数。
数据范围
1≤n≤105,
1≤ai,bi≤2*109
输入样例:
2
3 6
4 6
输出样例:
3
2
最大公约数:欧几里得算法【辗转相除法】
d | a && d | b ==> d | (a+b) ,d | (ax + by)
最核心 gcd(a,b) == gcd(b,a%b)
证明:a mod b = a - 向下取整[a/b] * b = a - c * b
d | b && d | a + c * b ----- > d | a -----> 最核心成立
库函数 __gcd(a,b) ;两个下杠
int my_gcd(int a,int b)
{
if(a%b == 0)return b;
return gcd(b,a%b);
}
int gcd_1(int a,int b)
{
//我写成if(a%b == 0)return b;即下一步b变为0之前,b已是最大公约数,输出
if(b == 0) return a; //a%b == 0也就是b = 0
return gcd(b,a % b);
}
int gcd(int a,int b)
{
return b ? gcd(b,a%b) : a;
}
int main()
{
int n;
scanf("%d",&n);
while(n --)
{
int a,b;
scanf("%d\n",&a,&b);
printf("%d\n",gcd(a,b));
}
return 0;
}