博客园 首页 私信博主 显示目录 隐藏目录 管理

数论

数学入门

数论

快速幂

  • \(\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{看一道题 [HNOI2008]越狱} \]

\(\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{上面窝们已经证明了欧几里得定理,那么下面来看这样一个问题}\)

\[ax + by = gcd(a, b) \]

\(\text{求最小整数x, y}\)

\(\text{根据上面的欧几里得定理,窝们可以知道这个方程等价于}\)

\[bx + (a \mod b)y = gcd(a, a \mod b)\text{(其中}a \mod b = a - \lfloor \frac{a}{b} \rfloor * b) \]

\(\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{的境地,哪这时的一组特解解为}\)

\[x = 1, y = 0 \]

  • \(\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{解方程组}\)

\[\begin{cases} x \equiv a_1 \pmod{p_1}\\ x \equiv a_2 \pmod{p_2}\\ .\\ .\\ .\\ x \equiv a_n \pmod{p_n}\\ \end{cases} \]

\(\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)

  • 解如下方程组:

\[\begin{cases} x \equiv a_1 \pmod{p_1}\\ x \equiv a_2 \pmod{p_2}\\ .\\ .\\ .\\ x \equiv a_n \pmod{p_n}\\ \end{cases} \]

  • \(\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;
	}

\[\text{带修,后续要放一些题} \]

posted @ 2020-10-18 11:06  Flash_plus  阅读(93)  评论(0编辑  收藏  举报