质数
- 0和1既不是质数也不是合数
acwing866试除法判定质数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
bool isPrime(int x){
if(x < 2) return false;
for (int i = 2; i <= x / i; i ++ ){
if(x % i == 0) return false;
}
return true;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
if(isPrime(x)) printf("Yes\n");
else printf("No\n");
}
}
acwing867分解质因数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
vector<PII> divide(int x){
vector<PII> v;
for (int i = 2; i <= x / i; i ++ ){
if(x % i == 0){
int c = 0;
while (x % i == 0){
c ++;
x /= i;
}
v.push_back({i, c});
}
}
if(x > 1) v.push_back({x, 1});
return v;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
auto v = divide(x);
for(PII p: v) printf("%d %d\n", p.first, p.second);
printf("\n");
}
}
acwing868筛质数
埃氏筛选法(简单易懂效率还行)
- 埃氏筛选法:若i是质数,则n倍i不是质数
/**
* 埃氏筛选法:若i是质数,则n倍i不是质数
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
bool st[N];
int n;
int primes(){
int res = 0;
for (int i = 2; i <= n; i ++ ){
if(!st[i]){
//若i是质数
res ++;
//i的倍数都不是质数
for (int j = 2; j * i <= n; j ++ ) st[i * j] = 1;
}
}
return res;
}
int main(){
scanf("%d", &n);
printf("%d", primes());
}
线性筛法
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int primes[N];
int cnt;
bool st[N];
int n;
void line(int n){
for (int i = 2; i <= n; i ++ ){
//没被筛选过,是质数
if(!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; j ++ ){
//把primes[j]倍i的数筛去,跟埃氏筛选法差不多
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
cnt = 0;
scanf("%d", &n);
line(n);
//prime[0~cnt-1]都是质数
printf("%d", cnt);
}
约数
acwing869试除法求约数
- 核心:若i能整除n,则n/i也能整除n
/**
* 核心:若i能整除n,则n/i也能整除n
**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
vector<int> divide(int x){
vector<int> v1, v2;
for (int i = 1; i <= x / i; i ++ ){
if(x % i == 0){
v1.push_back(i);
if(x / i != i) v2.push_back(x / i);
}
}
for (int i = v2.size() - 1; i >= 0; i -- ) v1.push_back(v2[i]);
return v1;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
auto v = divide(x);
for (int i = 0; i < v.size(); i ++ ) printf("%d ", v[i]);
printf("\n");
}
}
acwing870约数个数
任
何
一
个
整
数
d
都
可
以
表
示
为
若
干
质
数
的
乘
积
,
即
任何一个整数d都可以表示为若干质数的乘积,即
任何一个整数d都可以表示为若干质数的乘积,即
d
=
a
1
b
1
∗
a
2
b
2
∗
.
.
.
∗
a
n
b
n
,
其
中
a
i
为
质
数
,
b
i
是
指
数
d = a_1^{b1}*a_2^{b2}*...*a_n^{bn},其中a_i为质数,b^i是指数
d=a1b1∗a2b2∗...∗anbn,其中ai为质数,bi是指数
则
约
数
个
数
之
和
为
:
则约数个数之和为:
则约数个数之和为:
(
b
1
+
1
)
(
b
2
+
1
)
∗
.
.
.
∗
(
b
n
+
1
)
(b1 + 1)(b2 +1)*...*(bn +1)
(b1+1)(b2+1)∗...∗(bn+1)
因此本题做法是分解质因数,再套公式
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
unordered_map<int, int> m;
void divide(int x){
for (int i = 2; i <= x / i; i ++ ){
if(x % i == 0){
int cnt = 0;
while(x % i == 0){
cnt++;
x /= i;
}
m[i] += cnt;
}
}
if(x > 1) m[x] += 1;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
divide(x);
}
LL res = 1;
for(auto e: m) res = (LL)res * (e.second + 1) % mod;
printf("%d", res);
}
acwing871约数之和
约
数
乘
积
之
和
约数乘积之和
约数乘积之和
(
a
1
0
+
a
1
1
+
.
.
.
+
a
1
b
1
)
∗
(
a
2
0
+
a
2
1
+
.
.
.
+
a
2
b
2
)
∗
.
.
.
∗
(
a
n
0
+
a
n
1
+
.
.
.
+
a
n
b
n
)
(a_1^0+ a_1^1+...+a_1^{b1})*(a_2^0+ a_2^1+...+a_2^{b2})*...*(a_n^0+ a_n^1+...+a_n^{bn})
(a10+a11+...+a1b1)∗(a20+a21+...+a2b2)∗...∗(an0+an1+...+anbn)
分解质因数,再套公式
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
unordered_map<int, int> m;
void divide(int x){
for (int i = 2; i <= x / i; i ++ ){
if(x % i == 0){
int cnt = 0;
while(x % i == 0){
cnt++;
x /= i;
}
m[i] += cnt;
}
}
if(x > 1) m[x] += 1;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
divide(x);
}
LL res = 1;
for (auto p: m){
int a = p.first, b = p.second;
LL t1 = 0;
for (int k = 0; k <= b; k ++ ){
LL t2 = 1;
for (int z = 1; z <= k; z ++ ){
t2 = t2 * a % mod;
}
t1 = t1 % mod + t2 % mod;
}
res = res * t1 % mod ;
}
printf("%d", res);
}
acwing872最大公约数
欧几里得算法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int gcd(int a, int b){
return b > 0 ? gcd(b, a % b): a;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", gcd(a, b));
}
}
欧拉函数
acwing873欧拉函数
分解质因数+套公式
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
unordered_map<int, int> divide(int x){
unordered_map<int, int> m;
for (int i = 2; i <= x / i; i ++ ){
if(x % i == 0){
int cnt = 0;
while(x % i == 0) cnt++, x/= i;
m[i] = cnt;
}
}
if(x > 1) m[x] += 1;
return m;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int x;
scanf("%d", &x);
unordered_map<int, int> m = divide(x);
LL t1 = x, t2 = 1;
for(auto p: m){
t1 *= (p.first - 1);
t2 *= p.first;
}
printf("%d\n", t1 / t2);
}
}
acwing874筛法求欧拉函数
1.若i是质数,则1~i-1均与i互质,即phi[i]=i-1
2.当
i
%
p
r
i
m
e
s
[
j
]
=
=
0
i\%primes[j]==0
i%primes[j]==0:primes[j]是i的最小质因子,也是primes[j]*i的最小质因子。因为
1
−
1
p
r
i
m
e
s
[
j
]
1-\frac{1}{primes[j]}
1−primes[j]1在phi[i]计算过了,只需乘primes[j]倍就是
p
h
i
[
p
r
i
m
e
s
[
j
]
∗
i
]
phi[primes[j] * i]
phi[primes[j]∗i],即
p
h
i
[
p
r
i
m
e
s
[
j
]
∗
i
]
phi[primes[j] * i]
phi[primes[j]∗i]=phi[i]*primes[j]
3.当i % primes[j] != 0:primes[j]不是i的质因数,只是primes[j]*i的最小质因子,因此不仅需要乘primes[j]倍,还需要补上
1
−
1
p
r
i
m
e
s
[
j
]
1-\frac{1}{primes[j]}
1−primes[j]1,即phi[primes[j]*i]=phi[i]*(primes[j] - 1)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int phi[N], primes[N], cnt;
bool st[N];
int n;
void getPrimes(int x){
phi[1] = 1;
for (int i = 2; i <= n; i ++ ){
if(!st[i]){
phi[i] = i - 1;
//存下来
primes[cnt++] = i;
}
for (int j = 0; primes[j] <= n / i; j ++ ){
st[primes[j] * i] = true;
if(i % primes[j] == 0){
phi[primes[j] * i] = primes[j] * phi[i];
break;
}
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
}
}
int main()
{
cnt = 0;
scanf("%d", &n);
getPrimes(n);
LL res = 0;
for (int i = 1; i <= n; i ++ ) res += phi[i];
printf("%lld", res);
}
快速幂
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int qmi(int a, int k, int p){
a = a % p;
int res = 1 % p;
while (k > 0){
if(k & 1 == 1) res = (LL)res * a % p;
a = (LL) a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
int a, k, p;
while (n -- > 0){
scanf("%d%d%d", &a, &k, &p);
printf("%d\n", qmi(a, k, p));
}
}
acwing876快速幂求逆元
有逆元的前提
- 整数b,m互质
- 满足
b
∣
a
b|a
b∣a
定义:若满足 a / b ≡ a ∗ x ( m o d n ) a/b \equiv a * x(mod n) a/b≡a∗x(modn),则称x为b的模m的乘法逆元
注意:当m为质数时, b m − 2 b^{m-2} bm−2即为b的乘法逆元
总结:求a*b同余a*x模m,若m能整除a,无解,否则就是qmi(a,m-2,m)(当m为质数)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int qmi(int a, int k, int p){
a = a % p;
int res = 1 % p;
while(k > 0){
if(k & 1 == 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
int a, p;
scanf("%d%d", &a, &p);
if(a % p == 0) printf("impossible\n");
else printf("%d\n", qmi(a, p - 2, p));
}
}
扩展欧几里得算法
扩展欧几里得算法k可以求出一组
x
i
,
y
i
x_i,y_i
xi,yi,使其满足
a
i
x
i
+
b
i
y
i
=
g
c
d
(
a
i
,
b
i
)
a_ix_i+b_iy_i=gcd(a_i,b_i)
aixi+biyi=gcd(ai,bi)
当
b
=
0
时
a
x
+
b
y
=
a
故
而
x
=
1
,
y
=
0
当 b=0时 ax+by=a 故而 x=1,y=0
当b=0时ax+by=a故而x=1,y=0
acwing877
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int exgcd(int a, int b, int &x, int &y){
if(b == 0){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main(){
int n;
scanf("%d", &n);
while (n -- > 0){
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
}
acwing878线性同余方程
1.
a
∗
x
≡
b
(
m
o
d
m
)
等
价
于
a
∗
x
−
b
是
m
的
倍
数
,
设
为
−
y
倍
,
则
有
a
∗
x
+
m
y
=
b
a * x\equiv b(mod \ m) 等价于a *x - b是m的倍数,设为-y倍,则有 a*x + my = b
a∗x≡b(mod m)等价于a∗x−b是m的倍数,设为−y倍,则有a∗x+my=b
2.
根
据
裴
蜀
定
理
,
当
且
仅
当
g
c
d
(
a
,
m
)
∣
b
时
有
解
根据裴蜀定理,当且仅当gcd(a,m)|b时有解
根据裴蜀定理,当且仅当gcd(a,m)∣b时有解
3.
扩
展
欧
几
里
得
算
法
可
以
求
出
一
组
x
0
,
y
0
,
即
a
∗
x
0
+
m
y
0
=
g
c
d
(
a
,
m
)
=
d
;
要
让
d
变
为
b
,
整
个
式
子
需
要
乘
b
d
;
所
以
x
=
x
0
∗
b
d
扩展欧几里得算法可以求出一组x_0,y_0,即a*x_0+my_0=gcd(a,m)=d;要让d变为b,整个式子需要乘\frac{b}{d};所以x=x_0*\frac{b}{d}
扩展欧几里得算法可以求出一组x0,y0,即a∗x0+my0=gcd(a,m)=d;要让d变为b,整个式子需要乘db;所以x=x0∗db
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y){
if(b == 0){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main(){
int n;
scanf("%d", &n);
while (n -- > 0){
int a, b, m, x, y;
scanf("%d%d%d", &a, &b, &m);
int d = exgcd(a, m, x, y);
if(b % d != 0) printf("impossible\n");
else printf("%d\n", (LL)x * b / d % m);
}
}
组合数
acwing885
C a b = C a − 1 b − 1 + C a − 1 b C_{a}^{b}=C_{a-1}^{b-1}+C_{a-1}^{b} Cab=Ca−1b−1+Ca−1b
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
typedef long long LL;
LL f[N][N];
int main(){
//a
for (int i = 0; i < N; i ++ ){
//b
for (int j = 0; j <= i; j ++ ){
//c(n, 0) = 1
if(j == 0) f[i][j] = 1;
else f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % mod;
}
}
int n;
scanf("%d", &n);
while (n -- ){
int a, b;
scanf("%d%d", &a, &b);
printf("%lld\n", f[a][b]);
}
}
acwing886
C
a
b
=
a
!
b
!
∗
(
a
−
b
)
!
=
a
!
∗
b
!
−
1
∗
(
a
−
b
)
!
−
1
(
q
−
1
表
示
q
的
逆
元
)
C_{a}^{b}=\frac{a!}{b!*(a-b)!}=a! *{b!}^{-1}*{(a-b)!}^{-1}(q^{-1}表示q的逆元)
Cab=b!∗(a−b)!a!=a!∗b!−1∗(a−b)!−1(q−1表示q的逆元)
1e9+7是质数,b的逆元为
b
p
−
2
b^{p-2}
bp−2
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;
LL fact[N], infact[N];
int qmi(int a, int k, int p){
a = a % p;
int res = 1;
while (k > 0){
if(k & 1 == 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main(){
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ ){
fact[i] = (LL)fact[i - 1] * i % mod;
//i的逆元
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
int n;
scanf("%d", &n);
while (n -- > 0){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[a - b] % mod * infact[b] % mod);
}
}
acwing887
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int qmi(int a, int k, int p) {
a = a % p;
int res = 1;
while (k > 0)
{
if (k & 1 == 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
LL C(LL a, LL b, LL p){
LL res = 1;
for (int i = 1, j = a; i <= b; i ++, j-- ){
res = res * j % p;
res = res * qmi(i, p - 2, p) % p;
}
return res % p;
}
LL lucas(LL a, LL b, LL p){
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- > 0){
LL a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
printf("%d\n", lucas(a, b, p));
}
}
acwing889
卡特兰数:
C
2
n
n
−
C
2
n
n
−
1
=
C
2
n
n
n
+
1
C_{2n}^n - C_{2n}^{n-1} = \frac{C_{2n}^n}{n+1}
C2nn−C2nn−1=n+1C2nn
跟上题一样
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7;
typedef long long LL;
int qmi(int a, int k, int p) {
a = a % p;
int res = 1;
while (k > 0)
{
if (k & 1 == 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
int res = 1;
for (int i = 1, j = n + 1; i <= n ;i ++, j++ ){
res = (LL)res * j % mod;
res = (LL)res * qmi(i, mod - 2, mod) % mod;
}
res = (LL) res * qmi(n + 1, mod - 2, mod) % mod;
printf("%d", res);
}
容斥原理
加上减多的,减少加多的
能被整除的数
#include<iostream>
using namespace std;
typedef long long LL;
const int N = 20;
int p[N], n, m;
int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) scanf("%d", &p[i]);;
int res = 0;
//枚举每个状态,
for(int i = 1; i < 1 << m; i++) {
//选中集合对应质数的乘积
int t = 1;
//选中的集合数量
int s = 0;
//枚举当前状态的每一位
for(int j = 0; j < m; j++){
//选中一个集合
if(i >> j & 1){
//乘积大于n, 则n/t = 0, 跳出这轮循环
if((LL)t * p[j] > n){
t = -1;
break;
}
s++; //有一个1,集合数量+1
t *= p[j];
}
}
if(t == -1) continue;
//选中奇数个集合, 则系数应该是1, n/t为当前这种状态的集合数量
if(s & 1 == 1) res += n / t;
//反之则为 -1
else res -= n / t;
}
printf("%d", res);
return 0;
}
Nim游戏
acwing891
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
int r = 0;
while (n -- ){
int t;
scanf("%d", &t);
r ^= t;
}
if(r == 0) printf("No");
else printf("Yes");
}
acwing892
#include <iostream>
using namespace std;
int main()
{
int n, r = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ){
int x;
scanf("%d", &x);
if(i % 2 != 0) r ^= x;
}
if(r == 0) printf("No");
else printf("Yes");
}