(a*b)%p = (a%p)(b%p)%p
如果模较大,a%p * b%p 可能会超出long long 的范围可以使用快速乘法
ll fastMul(ll a, ll b, ll p) {
a %= p;
ll ans = 0;
while(b > 0) {
if(b & 1) ans = (ans + a) % p;
b >>= 1;
a = (a + a) % p;
}
return ans;
}
逆元
a/b%c = ab^-1%c
如果c是素数,有下面定理
费马小定理:设b是一个整数,c是一个素数,二者互质,那么b^c-1 =1(modc)
改写一下 bb^c-2 = 1(modc)
所以取b^-1 = b^c-2即可
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
ll lcm (ll a, ll b) {
return a / gcd(a, b) * b;
}
求ax + by = c的解
1、上述方程有解的充要条件是c 是gcd(a, b) 的倍数
2、所有解为:x = c / d * x0 + b / d * t, y = c / d * y0 - a / d * t
int extgcd(int a, int b, int& x, int& y) {
int d = a;
if(b != 0) {
d = extgcd(b, a % b, y, x);
y -= (a / b) * x;
}else {
x = 1;
y = 0;
}
return d;
}
扩展欧几里得求逆元
求·b^-1 使得 b*b^-1 = 1(mod c),实质上是求解方程bx + cy = 1中的x,可以用欧几里得算法求解。
bool isPrime(ll n) {
if(n == 1) return false;
for(ll i = 2; i * i <= n; i++) {
if(n % i == 0) return false;
return true;
}
}
埃氏筛:复杂度O(NlgN)
void getPrime(bool p[], int n) {
for(int i = 1; i <= n; i++) p[i] = true;
p[1] = false;
for(int i = 2; i <= n; i++) {
if(p[i]) {
for(int j = i + i; j <= n; j += i) p[j] = false;
}
}
}
用下面的方法可以保证每个合数只被它最下的素因子筛掉一遍:复杂度O(N)
ll getPrime(ll n, bool vis[], ll prime[]) {
ll tot = 0;
for(ll i = 1; i <= n; i++) vis[i] = 0;
for(ll i = 2; i <= n; i++) {
if(!vis[i]) prime[tot++] = i;
for(ll j = 0; j < tot; j++) {
if(prime[j] * i > n) break;
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
return tot;
}
求组合数,如果n, m不大,可以开O(N^2)的空间,利用杨辉恒等式来预处理组合数表
const ll mo = 1e9 + 7;
ll c[1005][1005];
void getC(int n) {
for(int i = 0; i <= n; i++) {
for(int j = 0; j <= i; j++) {
if(j == 0 || j == i) c[i][j] = 1;
else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
}
}
在竞赛中,常常需要计算组合数取余,如果n, m很小可以用C++的库函数 double tgamma(double x)
#include
ll C(ll n, ll m) {
return (ll)round(tgamma(n + 1) / tgamma(m + 1)/tgamma(n - m + 1));
}