数论
数学入门
数论
快速幂
- \(\text{求} a^b \pmod{p}\)
\(\text{当b很大的时候显然不能枚举,窝们换一种方法思考}\)
\(\text{窝们将b分奇偶讨论}\)
-
\(\text{当b为奇数时,} a ^ b = {a ^ {b / 2}} ^ 2 * a\)
-
\(\text{当b为偶数时,} a ^ b = {a ^ {b / 2}} ^ 2\)
\(\text{那么窝们每次先递归求出} a^{b / 2}\) \(\text{然后分情况算就好}\)
\(\text{上部分代码}\)
int a, b, p, ans = 1;
while(b){
if(b & 1) ans = ans * a % p;
a = a * a % p, b >>= 1;
}
//ans即为答案
\(\text{solution:}\)
\(\text{直接做的话是不是要分很多种情况而且还要容斥,不妨考虑反过来做}\)
\(\text{窝们考虑不会发生越狱的情况,那么每个位置与他相邻两个信仰宗教一定不同}\)
\(\text{那么当前位置有m种选择则剩下的位置会有}m - 1\text{种选择}\)
\(\text{所以答案为} m ^ n - m * {(m - 1)} ^ {n - 1}\)
质数(~~~~)
\(\text{质数的定义就不说了,记一下质数的筛法}\)
\(\text{首先判断一个数是否是质数注意} \sqrt{n}\) \(\text{的写法}\)
- \(\text{埃氏筛法复杂度} O(n loglog_n)\)
\(\text{找到每一个质数将他的倍数打标记, 代码就不贴了}\)
- \(\text{欧拉筛(线性筛)复杂度} O(n)\)
\(\text{窝们可以发现埃氏筛法种有很多数是重复筛的,于是窝们可以用最小的素数去把每一位筛掉}\)
\(\text{窝们具体看代码}\)
is_prime[1] = 0;
mem(is_prime, true);
for(int i = 2; i <= n; i++){
if(is_prime[i]) prime[++cnt] = i;
for(int j = 1; i * prime[j] <= n && j <= cnt; j++){
is_prime[i * prime[j]] = 0;
if(i % prime[j] == 0) break;//其他的数留给后面的筛
}
}
欧几里得定理(GCD)
- \(\text{记} gcd(a, b) \text{为a, b的最大公约数} (a \leq b)\)
\(\text{窝们会发现一些优美的性质:}\)
\(gcd(a, b) = gcd(a, a + b) = gcd(a, b - a)\)
\(\text{为什么呢?}\)
\(\text{事实上,窝们可以证明:} gcd(a, b) = gcd(a, b + t * a) (t \leq \lfloor \frac{b}{a} \rfloor)\)
- \(\text{窝们先证明} gcd(a, b) | gcd(a, b + t * a)\)
\(\text{令} gcd(a, b) = d, a = dx, b = dy\)
\(gcd(a, b) \equiv 0 \pmod{d}\)
\(gcd(a, b + t * a) \equiv gcd(dx, dy + t * dx) \equiv dgcd(x, y + tx) \equiv 0 \pmod{d}\)
\(\text{得证,反过来一样地证一遍就可以得出} gcd(a, b) = gcd(a, b + t * a)\)
\(\text{实际上就有}\) $gcd(a, b) = gcd(b, a \(%\) b) \text{就是辗转相除法}$
\(\text{窝们会发现每次取模至少缩小一半所以复杂度是}\) \(O(log(n))\)
扩展欧几里得定理
\(\text{上面窝们已经证明了欧几里得定理,那么下面来看这样一个问题}\)
\(\text{求最小整数x, y}\)
\(\text{根据上面的欧几里得定理,窝们可以知道这个方程等价于}\)
\(\text{把柿子化开有} ay + b(1 - \lfloor \frac{a}{b} \rfloor) x = gcd(a, a \mod b)\)
\(\text{假设这个柿子的解为}x_0,y_0 \text{哪原方程的解为} x = y_0, y = x_0 - \lfloor \frac{a}{b} \rfloor * x_0\)
\(\text{那么最后已知递归求会到}a = 1, b = 0 \text{的境地,哪这时的一组特解解为}\)
- \(\text{那么对于不定方程} ax + by = c(gcd(a, b) | c)\)
\(\text{先求出} ax + by = gcd(a, b) \text{的一组解}\)
\(\text{两边同乘} \frac{c}{gcd(a, b)} \text{有} ax * \frac{c}{gcd(a, b)} + by * \frac{c}{gcd(a, b)} = c\)
\(\text{就把x, y都乘上这个数不就好了}\)
逆元(模意义下的倒数)
\(\text{众所周知除以一个数等于乘他的倒数,但在模意义下可能会变成小数,怎么办呢?}\)
\(\text{这是窝们引入逆元的概念,即除以一个数等于乘以这个数在模p意义下的逆元}\)
\(\text{符号表达一下就是} x \equiv \frac{1}{x} \pmod{p}\Leftrightarrow ax \equiv 1 \pmod{p}\) \(\text{a就是x在模p意义下的逆元}\)
\(\text{下面引入几种逆元的求法:}\)
- \(\text{费马小定理求逆元}\)
\(\text{费马小定理即} a ^ {p - 1} \equiv 1 \pmod{p} \text{(p为质数)(不会证QAQ)}\)
\(\text{两边除以a有} a ^ {p - 2} \equiv \frac{1}{a} \pmod{p} \text{所以} a ^ {p - 2} \text{就是a在模p意义下的逆元}\)
\(\text{快速幂乱搞一下就好啦}\)
只适用于模数为质数的情况
- \(\text{exgcd求逆元}\)
\(\text{解方程组} ax \equiv 1 \pmod{p}\)
\(\Leftrightarrow ax + py \equiv 1 \pmod{p}\)
\(\text{就带到exgcd中求不定方程} ax + py = 1 \text{的解就好啦}\)
要注意的是,这里必须满足 gcd(a, p) = 1
- \(\text{线性递推逆元}\)
\(\text{窝们令} p = ki + b \text{则} kt + b \equiv 0 \pmod{p}\)
\(\text{移向有} b \equiv -ki \pmod{p}\)
\(\text{两边同时除以i, b有} \frac{1}{i} \equiv - \frac{k}{b} \pmod{p} \text{其中}k = \lfloor \frac{p}{i} \rfloor, b = p \mod i\)
\(\text{代码如下:}\)
inv[1] = 1;
rep(i, 2, n) inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
- \(\text{递推阶乘逆元}\)
\(\text{注意到阶乘有} f[i + 1] \equiv f[i] * (i + 1) \pmod{p}\)
\(\frac{1}{f[i + 1]} \equiv \frac{1}{f[i]} * \frac{1}{i + 1} \text{所以先线性递推再随便乱搞就好了}\)
中国剩余定理(孙孙定理)
\(\text{解方程组}\)
\(\text{其中} gcd(p_1, p_2, ..., p_n) = 1\)
\(\text{考虑构造答案}\)
\(\text{令}M = \prod_{i = 1} ^ n p_i, M_i = \frac{M}{p_i}, t_i \text{为} M_i \text{在模}p_i \text{意义下的逆元}\)
\(\text{会发现}t_i * M_i * a_i \equiv a_i \pmod{p_i}\)
\(\text{对于} \forall j \not = i, pj | M_i \therefore t_i * M_i * a_i \equiv 0 \pmod{p_j}\)
\(\therefore Ans = \sum_{i = 1} ^ n t_i * M_i * a_i\)
\(\text{要最小就模M一下就好}\)
扩展中国剩余定理(EXCRT)
- 解如下方程组:
- \(\text{(其中模数不一定互质)}\)
\(\text{对于这个方程,窝们不能直接套用CRT的方法来求解}\)
\(\text{因为上面窝们构造的方程中有一步是这样的}\)
\(\text{对于} \forall \text{i窝们要找到x使得} M_i * x \equiv 1 \pmod{p_i}\)
\(\text{对于这个同余方程,窝们是通过exgcd来求解x回想我们求解的过程}\)
\(\text{是通过建立不定方程} M_ix+p_iy=1 \text{来求解答案}\)
\(\text{但是前提是要} gcd(M_i, p_i) = 1\)
\(\text{这里的} p_i \text{不两两互质就不一定能达到这个前提}\)
\(\text{窝们考虑换一种方法来求解}\)
\(\text{先单独考虑前两项则有}\)
\(\text{令} x = p_1 * k_1 + a_1 = p_2 * k_2 + a_2\)
\(p_2 * k_2 - p_1 * k_1 = a_1 - a_2\)
\(\text{换一下符号有} p_2 * k_2 + p_1 * k_1 = a_1 - a_2\)
\(\text{像极了exgcd求不定方程} ax+by=gcd(a, b) \text{的形式}\)
\(\text{所以我们先求出不定方程为} p_2 * k_2 + p_1 * k_1 = gcd(p_1, p_2) \text{的解}\)
\(\text{然后将得到的} k_1 k_2 \text{乘上} \frac{a_1 - a_2}{gcd(p_1, p_2)} \text{就是一组解}\)
- \(\text{注意如果} gcd(p_1, p_2) \not| a_1 - a_2 \text{则无解}\)
\(\text{然后我们就可以反推出一组特解} x_0\) \(\text{满足第一二个柿子}\)
\(x_0 = -k_1 * p_1 + a_1 \text{(换了符号所以k等价于-k)}\)
\(\text{另外,窝们吧exgcd算出来的解乘的时候,注意是乘} \frac{a_1 - a_2}{gcd(p_1, p_2)}\)
\(\text{后面的}a_1 a_2 \text{一定不能反,否则就相当于要将x用y代替,可以试着反过来理解一下}\)
\(\text{然后可以知道通解就是} x = x_0 + t * lcm(p_1, p_2)\)
\(\text{改写一下柿子就成了} x \equiv x_0 \pmod{lcm(p_1, p_2)}\)
\(\text{那不就那这个柿子和下一个柿子去做一样的东西就好了!!!}\)
- \(\text{注意一下我们调换顺序的地方,代码实现的时候要注意,还要记得取模}\)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100000 + 5
#define maxm
#define ls (p << 1)
#define rs (p << 1 | 1)
#define lb(x) (x & (-x))
#define mid ((l + r) >> 1)
#define inf 123456789
#define iinf 10000000000000000
#define mod 1000000007
#define re register
#define il inline
#define int long long
#define rep(i, l, r) for(re int i = l; i <= r; ++i)
#define dep(i, l, r) for(re int i = r; i >= l; --i)
#define nxt(i, u) for(re int i = h[u]; i; i = e[i].next)
#define mem(f, x) memset(f, x, sizeof(f))
#define file(a) freopen(#a".in", "r", stdin); freopen(#a".out", "w", stdout);
typedef long long ll;
typedef double D;
int n, M, ans, x, y;
int p[maxn], a[maxn];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9'){ x = (x << 3) + (x << 1) + c - '0'; c = getchar();}
return x * f;
}
void exgcd(int a, int b, int &x, int &y){
if(!b){ x = 1, y = 0; return;}
exgcd(b, a % b, y, x), y -= a / b * x;
}
int gcd(int a, int b){ return !b ? a : gcd(b, a % b);}
int mul(int a, int b, int Mod){
int ans = 0;
while(b){
if(b & 1) ans = (ans + a) % Mod;
a = (a + a) % Mod, b >>= 1;
}
return ans;
}
signed main(){
//file(data);
n = read();
rep(i, 1, n) p[i] = read(), a[i] = read();
M = p[1], ans = a[1];
rep(i, 2, n){
exgcd(M, p[i], x, y); int Gcd = gcd(M, p[i]), c = (ans % p[i] - a[i] % p[i] + p[i]) % p[i];
if(c % Gcd){ puts("-1"); return 0;}
x = mul(x, c / Gcd, p[i]); //mul里面的x一定不能取模,否则就会算反
ans -= M * x, M = M / Gcd * p[i], ans = (ans % M + M) % M;
}
printf("%lld", (ans % M + M) % M);
return 0;
}
BSGS(大步小步)
\(\text{求} y^x \equiv z \pmod{p} \text{的最小正整数x(p为质数)}\)
\(\text{令} m = \sqrt p, x = mi - k (0 \leq k \leq m - 1)\)
\(\text{柿子变成} y ^ {mi} \equiv zy ^ k \pmod{p}\)
\(O(\sqrt p)\text{地暴力枚举k,开个map来存右边这个在模p意义下的值}\)
\(\text{然后窝们知道当p为质数是} \varphi(p) = p - 1\)
\(\text{由欧拉定理} a ^ b \equiv a ^ {b \% \varphi(p)} \pmod{p}\)
\(\text{所以当mi} \ge p\text{时就会重复地算}\)
\(\text{于是i只用枚举到m就好,然后在map里判断一下,复杂度} O(\sqrt n log n)\)
p = read(), b = read(), n = read(); M = sqrt(p);
if(b % p == 0 && n % p != 0){ puts("no solution"); return 0;}
if(n % p == 1){ puts("0"); return 0;}
if(b % p == 0 && n % p == 0){ puts("1"); return 0;}
rep(i, 1, M - 1){
int now = n * Q(b, i, p) % p; //printf("now here is %lld\n", now);
if(!a[now]) a[now] = i;
}
rep(i, 1, M){
int now = Q(b, i * M, p); //printf("asdsad %lld\n", now);
if(a[now]){ printf("%lld", i * M - a[now]); return 0;}
}
puts("no solution");
\(b = y,n = z\)
组合数学
卢卡斯定理
\(C ^ m_n \equiv C ^ {m \% p}_{n \%p} * C ^ {m / p}_{n / p} \pmod{p}\)
\(\text{不会证QAQ}\)
ll c(ll n, ll m, ll p){
if(m > n) return 0ll;
else return ((f[n] * qpow(f[m], p - 2, p)) % p * qpow(f[n - m], p - 2, p)) % p;
}
ll lucas(ll n, ll m, ll p){
if(m == 0) return 1ll;
return c(n % p, m % p, p) * lucas(n / p, m / p, p) % p;
}