1. 程式人生 > 實用技巧 >爾雅超星網課答案---【快捷查詢】

爾雅超星網課答案---【快捷查詢】

數學入門

數論

快速冪

  • \(\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{帶修,後續要放一些題} \]