一、数论
二、组合计数
三、高斯消元
四、简单博弈论
一、数论
(1)质数的判定—— 试除法 O(sqrt(n));
/*
质数(素数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数(约数)的自然数
1.严格大于1,本身大于等于2
2.除了1和自身之外没有其他因数,也就是只能整除这两个数
//暴力 O(n)
bool is_prime(int x)
{
if (x < 2) return false;//严格大于1
for (int i = 2; i < x; i ++ )//在2到n-1中存在某个数被x整除
if (x % i == 0)
return false;
return true;
}
*/
#include
using namespace std;
//优化 每个数的约数都是成对出现,如果i是n的约数,则n/i也是n的约数
bool is_prime(int x)
{
if (x < 2) return false;
/*
可以枚举每一对约数中较小的那个数即可,较小的约数的范围是1到根号n(从2开始枚举)
也可以这样理解,从小到大枚举每个可能是约数的数,循环条件是这个可能是约数的数
小于与它配对的那个约数
i<=sqrt(x)不推荐,因为每次都要执行这个较慢的操作
i*i<=n也不推荐,当n大道接近int最大值时,i*i可能溢出变成负数
时间复杂度sqrt(n)
*/
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
cin >> x;
if (is_prime(x)) puts("Yes");//输出且换行
else puts("No");
}
return 0;
}
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
(2)分解质因数——试除法
/*
//暴力 O(n)
void divide(int x)
{
for (int i = 2; i <= x; i ++ )
/*
从小到大枚举x的所有数,这里没有枚举质元素,会有问题吗?不会,因为枚举到i时,
x已经把i前面的(2到i-1)质因子全部都除干净了,此时x % i == 0的话,x是i的倍数,
且不包含任何2到i-1中的质因子,i中也不包含任何2到i-1中的质因子,所以i是质数
*/
if (x % i == 0)
{
int s = 0;
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl;
}
if (x > 1) cout << x << ' ' << 1 << endl;
cout << endl;
}
*/
/*
判定质数算法时间复杂度一定是根号n,但这道题时间复杂度不一定是根号n,
最好情况下是除一个数就除干净了,也就是除logn次
所以时间复杂度是logn到根号n之间
*/
#include
using namespace std;
void divide(int x)
{
//任意大于1的自然数,最多只有一个大于sqrt(n)的质因子,反证法可证
//所以先在2到sqrt(n)的范围去找质因子
for (int i = 2; i <= x / i; i ++ ){
/*
从2开始依次遍历,每次遍历到此时的i时,此时的x是已经把从2到i-1之间可能存在的质因子
全部除干净的x,如果这个时候的i仍然是此时的x的因数的话,那此时的i必然也是质数
*/ if (x % i == 0)
{
int s = 0;
//每次遍历到这个质因子时,x将这个质因子除干净
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl;
}
}
/*
如果此时已经把sqrt(n)之前的质因子全部除干净的n仍然大于1的话,那此时的n就是
那个大于sqrt(n)的质因子
*/
if (x > 1) cout << x << ' ' << 1 << endl;
cout << endl;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
cin >> x;
divide(x);
}
return 0;
}
void divide(int x)
{
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
int s = 0;
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl;
}
if (x > 1) cout << x << ' ' << 1 << endl;
cout << endl;
}
(3)
/*
朴素筛法 时间复杂度可以看做nlogn
当i==2时,运算n/2次,类推,运算次数
n/2+n/3+...n/n==n(1/2+1/3+...1/n)==n*调和级数==n*(ln n + c)
using namespace std;
const int N= 1000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{//从前往后看,把每一个数的倍数筛掉,这样剩下的数一定是质数
//如果p没被筛掉,说明2到p-1中没有谁的倍数是p,也就是没有p的约数,因此p为质数
if (!st[i]){
primes[cnt ++ ] = i;
}
for (int j = i + i; j <= n; j += i)
st[j] = true;
}
}
int main()
{
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;//输出cnt即可,不需要再加一,因为cnt已经++
return 0;
}
/*
线性筛法 O(n) 数据级别为1e7时,比上个方法快一倍
void 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;
}
}
}
*/
#include
using namespace std;
const int N= 1000010;
int primes[N], cnt;// primes[]存储所有素数
bool st[N];// st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
//n只会被它的最小质因子删掉
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
//对于任意一个合数x,假设pj为x最小质因子,当外层循环的i枚举到x/pj时,x一定会被筛掉
//也就是说当i枚举到x时,在此之前i一定会枚举到比x小的x/pj,而这个时候,x就会被筛掉
//每次筛的数都是用最小质因子去筛的
//primes[j] <= n / i是因为primes[j] * i要<=n,这样st[primes[j] * i]才有效
//思路是从小到大枚举所有的质数,去这个质数去筛掉它一定作为最小质因子的那个合数
//循环条件是这个合数primes[j] * i<=n也就是primes[j] <= n / i
st[primes[j]*i] = true;
//cout<> n;
get_primes(n);
cout << cnt << endl;
return 0;
}
(4)
#include
#include
#include
using namespace std;
vector get_divisors(int x)
{
vector res;
for (int i = 1; i <= x / i; i ++ )
//约数成对出现,枚举较小约数即可
if (x % i == 0)
{
res.push_back(i);
if (i != x / i) res.push_back(x / 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 x : res) cout << x << ' ';
cout << endl;
}
return 0;
}
(5)
/*
如果 N = p1^c1 * p2^c2 * ... *pk^ck
约数个数: (c1 + 1) * (c2 + 1) * ... * (ck + 1)
约数之和: (p1^0 + p1^1 + ... + p1^c1) * ... * (pk^0 + pk^1 + ... + pk^ck)
*/
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 110, mod = 1e9 + 7;
int main()
{
int n;
cin >> n;
unordered_map primes;
while (n -- )
{
int x;
cin >> x;
for (int i = 2; i <= x / i; i ++ )
while (x % i == 0)
{
x /= i;
primes[i] ++ ;
}
if (x > 1) primes[x] ++ ;
}
LL res = 1;
for (auto p : primes) res = res * (p.second + 1) % mod;//(c1 + 1)
cout << res << endl;
return 0;
}
/*
如果 N = p1^c1 * p2^c2 * ... *pk^ck
约数个数: (c1 + 1) * (c2 + 1) * ... * (ck + 1)
约数之和: (p1^0 + p1^1 + ... + p1^c1) * ... * (pk^0 + pk^1 + ... + pk^ck)
*/
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 110, mod = 1e9 + 7;
int main()
{
int n;
cin >> n;
unordered_map primes;
while (n -- )
{
int x;
cin >> x;
for (int i = 2; i <= x / i; i ++ )
while (x % i == 0)
{
x /= i;
primes[i] ++ ;
}
if (x > 1) primes[x] ++ ;
}
LL res = 1;
for (auto p : primes)
{
LL a = p.first, b = p.second;
LL t = 1;
while (b -- ) t = (t * a + 1) % mod;//(p1^0 + p1^1 + ... + p1^c1)
res = res * t % mod;
}
cout << res << endl;
return 0;
}
#include
#include
using namespace std;
// (a,b)==(b,a mod b)==(b,a - (a/b)*b)
// d能整除a,能整除b,则d也能整除a-c*b
// d能整除b,能整除a-c*b,则d也能整除能整除a-c*b+c*b==a
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int a, b;
cin>>a>>b;
cout<
#include
#include
using namespace std;
// (a,b)==(b,a mod b)==(b,a - (a/b)*b)
// d能整除a,能整除b,则d也能整除a-c*b
// d能整除b,能整除a-c*b,则d也能整除能整除a-c*b+c*b==a
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int a, b;
cin>>a>>b;
cout<