文章目录
- 质数:在大于1的整数中,如果只包含1和本身这两个约数,为质数,或者叫做素数。
- 约数
- 互质与欧拉函数
- 快速幂
- 欧几里得
- 博弈论入门
质数:在大于1的整数中,如果只包含1和本身这两个约数,为质数,或者叫做素数。
- 质数的判定——试除法: 时间复杂度:
bool is_prime(int n)
{
if(n < 2) return false;
//for(int i = 2; i * i <= n; ++i) //i会溢出,变成负数造成错误
for(int i = 2; i <= n/i; ++i)
if(n % i == 0)
return false;
return true;
}
- 分解质因数——从小到大枚举所有数: 时间复杂度:
void divide(int n)
{
//从小到大枚举所有数
//n中只包含一个大于sqrt(n)的质因子,反正即可
for(int i = 2; i <= n/i; ++i)
{
if(n % i == 0)
{
int s = 0; //次数
while(n % i == 0)
{
n /= i;
s++;
}
printf("%d %d\n", i, s); //
}
}
if(n > 1) printf("%d %d\n", n, 1); //如果最后>1 则该数即为 大于sqrt(n)的质因子
puts(" ");
}
- 埃氏筛法——枚举1~n的所有质数的倍数:时间复杂度:
void get_prime(int n)
{
for(int i = 2; i <= n; ++i)
{
if(!st[i])
{
primes[cnt++] = i;
for(int j = i+i; j <= n; j += i) st[j] = true;
}
}
}
- 线性筛法——用该数的最小质因子筛去时间复杂度:
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++)
{
cout<<primes[j]<<' '<<i<<" ---> "<<primes[j]*i<<endl;
st[primes[j]*i] = true;
//这行代码神奇地保证了每个合数只会被它的最小素因子筛掉,把复杂度降到了O(N)。
if(i % primes[j] == 0) break;
}
}
}
prime[]
数组中的素数是递增的,当i
能整除prime[j]
,那么i*prime[j+1]
这个合数肯定被prime[j]
乘以某个数筛掉。因为i
中含有prime[j]
,prime[j]
比prime[j+1]
小,即i=k*prime[j]
,那么i*prime[j+1]=(k*prime[j])*prime [j+1]=k’*prime[j]
,接下去的素数同理。所以不用筛下去了。因此,在第一次满足i%prime[j]==0
这个条件时,prime[j]
必定是prime[j]*i
的最小因子。
参考:
约数
- 试除法求一个数的所有约数——一个数的约数是成对出现的:时间复杂度:
vector<int> get_divisor(int n)
{
vector<int> res;
for(int i = 1; i <= n/i; ++i)
{
if(n % i == 0)
{
res.push_back(i);
if(i != n/i) res.push_back(n/i); //防止i*i为n,只要一个
}
}
sort(res.begin(), res.end());
return res;
}
- 约数个数
- 约数定理
- 最大公约数——欧几里得算法
/*
a >= b 时成立
a < b && b != 0时 gcd(a, b) == gcd(b, a%b) == gcd(b, a)成立
*/
int gcd(int a, int b)
{
return b ? gcd(b, a%b) : a;
}
互质与欧拉函数
互质:除了1以外,没有其他公约数时,称这两个数为互质数.
欧拉函数:1~N中与N互质的数的个数
- 公式求欧拉函数:时间复杂度:
int get_ola(int x)
{
int res = x;
for(int i = 2; i <= x/i; ++i)
{
if(x % i == 0)
{
res = res / x * (x-1); //因为只按照公式直接干的话,直接乘会越界,但是先除就不会越界
while(x % i == 0) x /= i;
}
}
if(x > 1) res = res / x * (x-1);
cout<<res<<endl;
}
- 线性筛法求欧拉函数:时间复杂度:
LL get_ola(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; ++i)
{
if(!st[i])
{
primes[cnt++] = i;
phi[i] = i-1; //如果一个数i是质数的,其欧拉函数就是i-1个//1、
}
for(int j = 0; primes[j] <= n/i; ++j)
{
st[primes[j]*i] = true;
if(i % primes[j] == 0) //break;
{
phi[primes[j]*i] = primes[j]*phi[i]; // 2、
break;
}
else
{
// phi[primes[j]*i] = primes[j]*phi[i] / primes[j] * (primes[j]-1);
phi[primes[j]*i] = phi[i] * (primes[j]-1); //3、
break;
}
}
}
LL res = 0;
for(int i = 1; i <= n; ++i)
res += phi[i];
return res;
}
参考:
874. 筛法求欧拉函数
快速幂
- 快速幂——反复平方法:时间复杂度:
//反复平方法
int qmi(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) //检查个位
res = (LL)res*a%p;
a = (LL)a*a%p;
b >>= 1; //把b的个位删掉
}
return res;
}
- 快速幂求逆元——欧拉定理、费马小定理
#include <iostream>
using namespace std;
typedef long long LL;
int n;
int a, b, p;
//反复平方法
int qmi(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) //检查个位
res = (LL)res*a%p;
a = (LL)a*a%p;
b >>= 1; //把b的个位删掉
}
return res;
}
int main()
{
cin>>n;
while(n--)
{
cin>>a>>p;
int res = qmi(a, p-2, p);
if(a%p) cout<<res<<endl;
else cout<<"impossible"<<endl;
}
return 0;
}
```
欧几里得
- 扩展欧几里得算法——裴蜀定理
int exgcd(int a, int b, int &x, int &y) //要求
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a%b, x, y); //已经求好
int z = x; x = y; y = z-y*(a/b); //得出参数的x、y
return d;
}
- 线性同余方程——扩展欧几里得算法
typedef long long LL;
int exgcd(int a, int b, int &x, int &y) //要求
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a%b, x, y); //已经求好
int z = x; x = y; y = z-y*(a/b); //得出参数的x、y
return d;
}
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
int a, b, m, x, y;
scanf("%d%d%d", &a, &b, &m);
int d = exgcd(a, m, x, y);
if(b%d) printf("impossible\n");
else
printf("%d\n", (LL)x*(b/d)%m);
// 不可以写成x/d*b, 因为上面保证d能整除b,但不能保证d能整除x
}
return 0;
}
博弈论入门
- Nim游戏
#include <iostream>
using namespace std;
int n;
int ans;
int main()
{
cin>>n;
int x;
while(n--)
{
cin>>x;
ans ^= x;
}
if(ans)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
return 0;
}
- 集合——Nim游戏
#include <iostream>
#include <cstring>
#include <unordered_map>
#include <algorithm>
using namespace std;
/*
mex(S):不属于该集合S的最小非负整数
SG(终点) = 0
SG(X)从后往前求出来的
n个图(石子)的起点的SG值异或起来!=0 先手必胜
*/
const int N = 110, M = 10010;
int n, m;
int s[N], f[M];
int sg(int x) //记忆化搜索
{
if(f[x] != -1) return f[x];
unordered_set<int> S;
for(int i = 0; i < m; ++i)
{
int sum = s[i];
if(x >= sum) S.insert(sg(x-sum));
}
for(int i = 0; ; i++)
if(!S.count(i))
return f[x] = i;
}
int main()
{
cin>>m;
for(int i = 0; i < m; ++i) cin>>s[i];
cin>>n;
memset(f, -1, sizeof(f));
for(int i = 0; i < n; ++i)
{
int x;
cin>>x;
res ^= sg(x);
}
if(res)
puts("Yes");
else
puts("No");
return 0;
}