數學學習筆記(持續更新中)


數學學習筆記

前言

要開始學數學(數論)了,於是特開一個專題來記錄一下相關知識和有關問題。

0.矩陣與快速冪

這里我認為與數學關系不是很大,於是一筆帶過吧。

矩陣,是 \(n\times m\) 個數 \(a_{i,j}\) 組成的數表,稱為 \(m\)\(n\) 列的矩陣。

記作: \(A=\begin{bmatrix}a_{1,1} & a_{2,1}&\cdots&a_{1,m}\\a_{2,1}&a_{2,2}&\cdots& a_{2,n}\\ \vdots&\vdots&\ddots&\vdots\\a_{n,1} & a_{n,2}&\cdots&a_{n,m} \end{bmatrix}\).

  • 特別定義:單位矩陣 \(I\) ,其中 \(a_{i,i} = 1\), 其余為 \(0\)
  • 其性質:任意次方等於自己,與任意矩陣相乘等於另一矩陣。

矩陣乘法

\(C=A\times B\)

  • \(A\)\(n\times r\) 的矩陣, \(B\)\(r\times m\) 的矩陣,那么 \(C\)\(n\times m\) 的矩陣。
  • 定義: \(C_{i,j} = \sum\limits_{k=1}^r A_{i,k}\times B_{k,j}\)

矩陣乘法滿足交換律,不滿足結合律。

另外,矩陣內可以內套矩陣。形如 \(P=\begin{bmatrix}A&B\\C&D\end{bmatrix}\) ,其中 \(A\)\(B\)\(C\)\(D\) 為矩陣。它仍然滿足矩陣乘法的規律。

矩陣快速冪

顧名思義,矩陣快速冪,就是矩陣的多次乘方。過程用快速冪優化。

模板:【【模板】矩陣快速冪】。

應用

可以用它來解決形如 \(F_n=p\times F_{n-1}+q\times F_{n-2}+\cdots\) 的遞推式。

甚至,形如 \(f(i,j) = \sum f(i,k)\times f(k,j)\) 的動態規划也可用其解決。

例題

P1349 廣義斐波那契數列

P3758 可樂







1.質數與約數

結論

  1. 對於一個足夠大的整數,不超過 \(N\) 的質數大約有 \(\dfrac N{\ln N}\) 個,即每 \(\ln N\) 個數中有一個質數。

  2. 算數基本定理:正整數 \(N\) 可以唯一分解成有限個質數的乘積,即 \(N=p_1^{c_1}p_2^{c_2}\cdots p_m^{c_m}\) ,其中 \(p\) 都是質數, \(c\) 都是正整數,且滿足 \(p_1 <p_2<\cdots<p_m\) .

  3. \(N\) 的正約數個數為 \(\prod\limits_{i=1}^m (c_i+1)\) .

  4. \(N\) 的所有正約數和為

    \[\prod \limits_{i=1}^m\begin{pmatrix}\sum\limits_{j=0}^{c_i} (p_i)^j\end{pmatrix} \]

對於上一個式子,其中有\(\sum\limits_{j=0}^{c_i} (p_i)^j\) .

它是一個等比數列的求和。

對於等比數列的求和,設首項為 \(a_1\) ,公比為 \(q\) ,其公式為: \(S_n = \dfrac{a_1(q^n-1)}{q-1}\) .

證明:

\[\begin{aligned}S_n=& \ a_1+& a_1\times q& + a_1\times q^2+\cdots+a_1\times q^{n-1} & \\S_n\times q= & & a_1\times q& + a_1\times q^2+\cdots+a_1\times q^{n-1}& + a_1\times q^n\end{aligned} \]

上下相減,移項,得到公式。

線性篩

先放代碼

bool vis[M];
int pri[N],cnt;
void Prime(){
	for(int i = 2 ; i <= n ; i ++){
		if(!vis[i]) pri[++cnt] = i;
		for(int j = 1 ; j <= cnt && i * pri[j] <= n ; j ++){
			vis[i*pri[j]] = 1;
			if(!(i % pri[j])) break;
		}
	}
}

下面來詳細解釋:

vis[i] 表示 \(i\) 這個數是否被篩去過(其實也可以用它來記錄某個數的最小質因子)。

其本質,是用每個數的最小質因子(即為 pri[j] )來篩去這個數。

為什么判斷break在后面?

  • 考慮用 \(2\) 篩去 \(4\) .

正確性與復雜度的證明

以下內容參照 歐拉篩篩素數 - 學委 的博客

設一合數 \(C\)(要篩掉)的最小質因數是 \(p1\),令 \(B = \dfrac C {p_1}\)\(C = B \times p_1\)),則 \(B\) 的最小質因數不小於 \(p_1\) 否則 \(C\) 也有這個更小因子)。那么當外層枚舉到 \(i = B\) 時,我們將會從小到大枚舉各個質數;因為 \(i = B\) 的最小質因數不小於 \(p_1\),所以 \(i\) 在質數枚舉至 \(p_1\) 之前一定不會 break這回\(C\) 一定會被 \(B \times p_i\) 刪去。

注意這個算法一直使用“某數×質數”去篩合數,又已經證明一個合數一定會被它的最小質因數 \(p_1\) 篩掉,所以我們唯一要擔心的就是同一個合數是否會被“另外某數 × \(p_1\) 以外的質數”再篩一次導致浪費時間。設要篩的合數是 \(C\),設這么一個作孽的質數為 \(p_x\),再令 \(A = \dfrac C {p_x}\)\(A\) 中一定有 \(p_1\) 這個因子。當外層枚舉到 \(i = A\),它想要再篩一次 \(C\),卻在枚舉 \(Prime[j] = p_1\) 時,因為 \(i \mod Prime[j] == 0\) 就退出了。因而 \(C\) 除了 \(p_1\) 以外的質因數都不能篩它。

例題

質數距離

UVA10140 Prime Distance

給定兩個整數 \(L,R\) ,求 \([L,R]\) 中相鄰兩個質數的差最大以及最小是多少,輸出這兩個質數。
\(1\leq L\leq R\leq2^{31}-1,0\leq R-L\leq 10^7\)

解析

\(L,R\) 很大,我們不能篩出所有的質數。

但是,可以篩出 \(\sqrt R\) 以內的質數,用這些質數來篩 \([L,R]\) 的質數,並存儲在 \(p[i-l]\) 中。

int n,m;
int pri[N];bool vis[N];
void Prime(){
	for(int i = 2 ; i <= 1e6 ; i ++){
		if(!vis[i]) pri[++m] = i;
		for(int j = 1 ; j <= m && i * pri[j] <= 1e6 ; j ++){
			vis[i*pri[j]] = 1;
			if(!(i % pri[j])) break;
		}
	} 
}	
int p[N];
signed main(){
	ll l,r;
	Prime();
	while(scanf("%lld %lld",&l,&r) != EOF){
		memset(vis,0,sizeof(vis)); n = 0; 
		if(l == 1)vis[0] = 1;
		for(int j = 1 ; j <= m ; j ++)
			for(int i = l/pri[j] ; 1ll * i * pri[j] <= r ; i ++)
				if(i > 1)
					vis[i*pri[j]-l] = 1;
		for(int i = l ; i <= r ; i ++)
			if(!vis[i-l]) p[++n] = i;
		if(n < 2){puts("There are no adjacent primes.");continue;}
		int mx1 = p[1], mx2 = p[2],
			mn1 = p[1], mn2 = p[2];
		for(int i = 3 ; i <= n ; i ++){
			if(p[i]-p[i-1] > mx2-mx1) mx2 = p[i], mx1 = p[i-1];
			if(p[i]-p[i-1] < mn2-mn1) mn2 = p[i], mn1 = p[i-1];
		}
		printf("%d,%d are closest, %d,%d are most distant.\n",mn1,mn2,mx1,mx2);
	}
	return 0;
}

不定方程

不定方程

求不定方程: \(\dfrac 1x+\dfrac1y=\dfrac1{n!}\)的正整數解 \((x,y)\) 的數目。

\(n\leq10^6\).

解析

先把式子改寫一下,變成 \(y=\dfrac{x\cdot n!}{x-n!}\)

\(t = x-n!\) , 則原式變為 \(y=n!+\dfrac {(n!)^2}t\) .

則問題轉化為:求出 \((n!)^2\) 的約數總數。

根據上述的結論,可知對於正整數 \(N\) ,其約數共有 \(\prod(c_i+1)\)

那么,對於正整數 \(N^2\) ,其約數共有 \(\prod(2\times c_i+1)\)

對於求 \(n!\) 的約數,我們有如下做法:

for(int i = 1 ; i <= m ; i ++)
		for(ll j = pri[i] ; j <= n ; j *= pri[i])
			(c[i] += n/j) %= mod;

下面詳細解釋一下:

比如我們考慮 \(n=9\) , 先提取出其 \(2\) 的倍數為 \(9!=\cdots\times2\times4\times6\times8\times\cdots\).

使用質因子 \(2\) 來篩:

\[2=2\times\cdots\\4=2\times2\times\cdots\\6=2\times\cdots\\8=2\times2\times2\times\cdots\\\cdots \]

形象化地,如下圖:

圖源:conprour

我們求和,相當於每次求一個豎列的和並相加。

用如上代碼,我們可以便捷地: \(+\) 紅(\(4\)),\(+\)綠(\(2\)\(\cdots\)

於是它成立。

ll n,k,ans;
void solve(){
	ll l,r;
	for(l = 1 ; l <= n ; l = r + 1){
		if(k/l) r = min(n,k/(k/l));
		else r = n;
		ans -= k/l * (l+r) * (r-l+1) >> 1;
	} 
}
signed main(){
	n = read(), k = read();
	ans = n * k;
	solve();
	printf("%lld",ans);
	return 0;
}

余數之和

給定 \(n,k\) ,求

\[G(n,k)=\sum_{i=1}^n k\bmod i \]

\(1\leq n,k\leq10^9\)

題解

已知: \(k\bmod i = k-\left\lfloor\dfrac ki\right\rfloor\)

那么,對於原式,就是求

\[\begin{aligned} G(n,k)&=\sum _{i=1}^n (k - \left\lfloor \dfrac ki\right\rfloor)\\ &=n\times k - \sum_{i=1}^n\left\lfloor\dfrac ki\right\rfloor \end{aligned} \]

其中, \(\sum \lfloor\frac ki\rfloor\) 如果暴力求,是 \(O(n)\) 的。

但是,用一些方法,我們可以讓它低至 \(O(\sqrt n)\)

整除分塊

整除分塊,可以解決如同: 求 \(\sum \lfloor\frac ki\rfloor\) 的問題。復雜度是 \(O(\sqrt n)\)

我們可以發現,對於 \(\lfloor\frac ki\rfloor\) 的式子,隨着 \(i\) 的增加,式子的值是類似階梯塊狀單調不降的。

結論:對於 \(\lfloor\frac ki\rfloor\) 取值相同的 \(i\) ,設其取值相同時 \(i\in[l,r]\) , 則對於確定的 \(l\) ,則 \(r = \left\lfloor\dfrac k{\left\lfloor\dfrac kl\right\rfloor}\right\rfloor\)

證明

懶得證明了,反正它是對的 QWQ 。


對於本題來說,在一個塊內,則是 \(\left\lfloor\dfrac kl\right\rfloor\times \sum\limits_{i=l}^ri = \left\lfloor\dfrac kl \right\rfloor\times \dfrac{(l+r)\times (r-l+1)}2\)

ll n,k,ans;
void solve(){
	ll l,r;
	for(l = 1 ; l <= n ; l = r + 1){
		if(k/l) r = min(n,k/(k/l));
		else r = n;
		ans -= k/l * (l+r) * (r-l+1) >> 1;
	}
}
signed main(){
	n = read(), k = read();
	ans = n * k;
	solve();
	printf("%lld",ans);
	return 0;
}

哈希函數(質因數分解)

\(x,y\) 的hash函數為:\(h=x\times y+x+y\)。對於給出一個 \(h\) 值,問有多少對 \((x,y)\) 滿足 \(\max(x,y)\leq h\)\(h,x,y\) 都為非負整數。

\(h\leq 10^8,T\leq10^4\)

題解

終於能不看題解做出數論的題了!

由於給定函數,可知 \(h \ge \max(x,y)\) 恆成立。於是不需考慮這個條件。

移項,變成 \(y = \dfrac{h-x}{x+1}\)

\(t = x +1\) ,則原式變為: \(y = \dfrac{h-t+1}t = \dfrac {h+1}t-1\)

則直接統計 \(h+1\) 的因子個數即可。

用這道題也可以練一下質因數分解

ll n;
bool vis[N];
int pri[N],pcnt;
void Prime(){
	for(int i = 2 ; i <= 1e4 ; i ++){
		if(!vis[i]) pri[++pcnt] = i;
		for(int j = 1 ; j <= pcnt && i * pri[j] <= 1e4 ; j ++){
			vis[i*pri[j]] = 1;
			if(!(i%pri[j])) break;
		}
	}
}
int cnt,c[N],p[N];
inline bool isprime(ll x){
	ll ed = sqrt(x);
	for(int i = 2 ; i <= ed ; i ++) if(!(x % i)) return 0;
	return 1;
}
void work(){
	ll x = n = read()+1;
	memset(c,0,sizeof(c));
	cnt = 0;
	for(int i = 1 ; i <= pcnt ; i ++){
		if(!(x % pri[i])) p[++cnt] = pri[i];
		while(!(x % pri[i])) c[cnt] ++ , x /= pri[i];
		if(x == 1) break;
	}
	ll ans = 1;
	if(x > 1e4 && isprime(x))p[++cnt] = x,c[cnt] = 1;
	for(int i = 1 ; i <= cnt ; i ++)
		ans *= (c[i]+1);
	printf("%lld\n",ans);
}
signed main(){
	Prime();
	int T = read();
	while(T--) work();
}

數字對

對於一個數字對 \((a,b)\) ,我們可以通過一次操作將其變為新數字對 \((a+b, b)\)\((a, a+b)\)

給定一正整數 \(n\) ,問最少需要多少次操作可將數字對 \((1, 1)\) 變為一個數字對,該數字對至少有一個數字為 \(n\)

題解

又是自己想出來的!

發現逆過程和更相減損法相同,於是直接枚舉 \((n,x)\)\(x\) ,進行更相減損法就可以。

注意:深搜需要進行最優性剪枝。

void solve(int a,int b,int dep){
	if(dep > ans) return;
	if(a == 1 && b == 1) {ans = min(ans,dep);return;}
	if(a == b) return;
	if(a < b) swap(a,b);
	solve(a-b,b,dep+1);
}
signed main(){
	n = read();
	if(n == 1){puts("0");return 0;}
	for(int i = 1 ; i < n ; i ++)
		solve(n,i,0);
	printf("%d",ans);
}

后續

看了一下題解的做法,題解還是更巧妙……

上述和我的相同,但是不同的部分在於,在輾轉相減的過程中,可以直接改為輾轉相除進行優化,時間效率顯著提升(其實並沒有?)

void solve(int a,int b,int dep){
	if(dep-1 > ans) return;
	if(!b){
		if(a == 1) ans = min(ans,dep-1);
		return;
	}
	solve(b,a%b,dep+a/b);
}
signed main(){
	n = read();
	if(n == 1){puts("0");return 0;}
	for(int i = 1 ; i < n ; i ++)
		solve(n,i,0);
	printf("%d",ans);
}

燈光控制

學校宿管有一套神奇的控制系統來控制寢室的燈的開關:

共有 \(n\) 盞燈,標號為 \(1\)\(n\) ,有 \(m\) 個標有不同質數的開關,開關可以控制所有標號為其標號倍數的燈,按一次開關,所有其控制的滅着的燈都點亮,所有其控制的亮着的燈將熄滅。現在,宿管可以無限的按所有開關,所有燈初始狀態為熄滅,請求出最多能點亮幾盞燈。

\(n\leq1000,m\leq\{n\texttt{以內的質數的總個數}\}\)

題解

沒有想出……

  1. 對於 \(i\leq \sqrt n\) ,所有的質數最多有 \(11\) 個,這里爆搜就可以。
  2. 對於 \(i\ge \sqrt n\) ,每個燈最多只會被唯一一個\(\ge\sqrt n\)\(i\) 覆蓋一次。這里在前一個條件完全做完后,依次枚舉即可。

對於2.,可以使用貪心。如果答案變大就一定保留它。(原因還是只會被修改一次)。

注意:所有的統計答案應該在 1.2.全部做完后進行,否則答案可能不會最優

int n,m;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000int pri[N];bool c[N],wrk[N]; int ans;void dfs(int cur,const int lim){	if(cur == lim+1){		memset(c,0,sizeof(c));		for(int i = 1 ; i <= lim ; i ++)			if(wrk[i])				for(int j = 1 ; j * pri[i] <= n ; j ++)					c[j * pri[i]] ^=  1;		int ret = 0;		for(int i = 1 ; i <= n ; i ++) ret += c[i];		for(int i = lim+1 ; i <= m ; i ++){			int delt = 0;			for(int j = 1 ; j * pri[i] <= n ; j ++)				delt += c[j*pri[i]] ? -1 : 1;			if(delt > 0){				ret += delt;				for(int j = 1 ; j * pri[i] <= n ; j ++)					c[j*pri[i]] ^= 1;			}		}		ans = max(ans,ret);		return;	}	wrk[cur] = 0;dfs(cur+1,lim);	wrk[cur] = 1;dfs(cur+1,lim);}void work(){	n = read(), m = read();	for(int i = 1 ; i <= m ; i ++) pri[i] = read();	sort(pri+1,pri+m+1);	int lim = upper_bound(pri+1,pri+m+1,sqrt(n))-pri-1;	ans = 0;	dfs(1,lim);	printf("%d\n",ans);}signed main(){	int T = read();	while(T--) work();}

最大公約數

\(n\) 個數字 \(a_1,a_2,\cdots,a_n\) ,求 \(\max\gcd(a_i,a_j)(i\neq j)\)

\(n\leq10000,1\le a_i\le 10^6\)

題解

自己做出來就是舒服!

開始有點想偏了,想到質因數分解去了……

這里,直接枚舉 \(\sqrt {a_i}\) 以下的 \(a_i\) 的因數,並給其另一個因數打上標記。最后找到最大的標記數 \(\ge2\) 的就是答案了。

signed main(){	n = read();	for(int i = 1 ; i <= n ; i ++) a[i] = read();	int mx = 0;	for(int i = 1 ; i <= n ; i ++){		int ed = sqrt(a[i]);		mx = max(mx,a[i]);		for(int j = 1 ; j <= ed ; j ++)			if(!(a[i] % j)) {				stk[j]++, stk[a[i]/j] ++;				if(j*j == a[i]) stk[j] --;			}	}	for(int i = mx ; i ; i --)		if(stk[i] > 1){			printf("%d",i);			return 0;		}}

總結

質數與約數算是數論的入門章節,其變化也不是很多。

主要知識就是算數基本定理篩法求質數除法分塊。而常常配合其他算法使用。

從這里入門,大概有了數論的思路:

得到式子,用技巧簡化計算。







2.同余問題

結論

一、基礎概念

  1. \(a|b\)\(a\) 能整除\(b\)
  2. \(a\equiv b\pmod m\)\(a\)\(b\) 在模 \(m\) 意義下同余。
  3. \(\varphi(n)\) :歐拉函數,表示 \([1,n]\) 中與 \(n\) 互質的數的個數。

二、同余

\(a\equiv b\pmod m\Leftrightarrow m|(a-b)\Leftrightarrow a=b+k\times m\)

同余有如下性質:

自反性、對稱性、傳遞性、同加/減/乘/冪 性(沒有同除性)。

另外:

  1. \(a\equiv x \pmod p,a\equiv x\pmod q\) ,其中 \(p,q\) 互質,那么 \(a\equiv x\pmod{(pq)}\) .
  2. \(\gcd(a,b) = 1\) ,則 \(\forall i,j(i\not\equiv j\pmod p),a\times i\not\equiv a\times j\pmod p\) .
  3. \(\gcd(a,n) = 1\) 時,\(\forall i(\gcd(i,j) = 1),\gcd(a\times i\bmod n,n0) = 1\) 0.

相關定理

費馬小定理

\(p\) 是質數,則對於任意整數 \(a\) ,有\(a^p\equiv a\pmod p\) .

推論:若 \(p\) 是質數00000,且\(p\nmid a\) 時, \(a^{p-1}\pmod p\) .

歐拉函數

  1. \(n=p^k\) ,則 \(\varphi(n) = p^k-p^{k-1}\) .
  2. 互質的兩個正整數 \(n,m\) ,那么 \(\varphi (nm)=\varphi(n)\times\varphi(m)\) .
  3. \(N = \prod\limits_{i=1}^n p_i^{k_i}\) ,則 \(\varphi(N) = N \times\prod\limits_{i=1}^n(1-\dfrac 1{p_i})\) .
  4. \(\varphi(N)=\prod\limits_{i=1}^n \varphi(p_i^{p_i})\)

歐拉定理

\(\gcd(a,n)=1\) 時,有 \(a^{\varphi(n)} \equiv 1\pmod n\) .

推論:若 \(\gcd(a,n)=1\) ,則 \(a^b\equiv a^{p\,\bmod\, \varphi(n)}\pmod n\) .

擴展歐拉定理:若 \(b\ge\varphi(n)\) ,則 \(a^b\equiv a^{b\,\bmod\,\varphi(n)+\varphi(n)}\pmod n\) .

根據歐拉定理,我們可以知道:\(p\) 質數且 \(\gcd(a,p)=1\) ,則 \(a^b\equiv a^{b\,\bmod\,(p-1)}\pmod p\) .


三、拓展歐幾里得算法

裴蜀定理

對於任意整數 \(a,b\) ,存在一對整數 \(x,y\) ,滿足 \(ax+by=\gcd(a,b)\) .

應用

\(ax+by=\gcd(x,y)\) 的解。

int exgcd(int a,int b,int& x,int &y){	if(!b){x = 1 , y = 0; retu000rn a;}	int g = exgcd(b,a%b,x,y);	int t = x; x = y , y = t - a/b * x;	return g;} 

對於 \(ax+by=c(c\leq\gcd(a,b))\) ,只有 \(c=\gcd(a,b)\) .

另外,若 \((x,y)\) 是原方程的解,那么 \((x\pm\dfrac b{\gcd(a,b)},y\mp\dfrac a{\gcd(a,b)})\) 也是解。

四、線性同余方程

形似 \(ax\equiv c\pmod b\) 的方程。

結論:當 \(\gcd(a,b)|c\) 時, \(ax\equiv c\pmod b\) 一定有解。反之,無解。

假設已知其中一個解 \(x=x_0\) ,那么該方程在模 \(b\) 意義下,有 \(\gcd(a,b)\) 個不同的解。其中 \(x_i=\Bigg(x_0+i\times \dfrac d{\gcd(a,b)}\Bigg)\bmod b\)

五、乘法逆元

如果 \(b,p\) 互質,且 \(b|a\) ,則存在一個整數 \(x\) ,使得 \(\dfrac ab\equiv ax\pmod p\) ,那么 \(x\) 稱為 \(b\) 在模 \(p\) 的意義下的乘法逆元,記作 \(b^{-1}\)

求法

  1. 對於 \(p\) 是質數,則可以便捷0地使用費馬小定理\(b^{-1}\equiv b^{p-2}\pmod p\) .
  2. 對於 \(p\) 非質數,可以使用拓展歐幾里得解同余式解決: \(bx\equiv 1\pmod p\Rightarrow bx+py=1\)
  3. 可以使用線性求逆元。設 \(r=b\bmod p\) ,則對於 \(b\) 的逆元,公式為 \(b^{-1}=-r^{-1}\times\left\lfloor\dfrac pb\right\rfloor\)
for(int i = 1 ; i <= n ; i ++)
	inv[i] = ((p-p/i*inv[p%i])%p+p)%p;

六、中國剩余定理

給定一個方程組:

\[\left\{\begin{matrix} x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_20} \\\cdots\\ x\equiv a_n\pmod{m_n} \end{matrix}\right. \]

\(m_1\cdots m_n\) 兩兩互質,則必定有解。

\(M=\prod m_i\)\(M_i=\dfrac M{m_i}\)\(t_iM_i\equiv 1\pmod{m_i}\) 。(注意是在模 \(m_i\) 下的逆元)

那么,其解為:\(\sum\limits_{i=1}^na_iM_it_i\) .

證明

\(p = \sum\limits_{i=1}^na_iM_it_i\) .

對於方程組的第 \(j\) 個式0子:

\[a_iM_it_i\equiv \left\{\begin{matrix}0\pmod {m_j} & ,& i\neq j.&\text{因為}M_i\text{中必有}m_j\text{這個因子}\\ a_i\pmod{m_j}&,&i=j.&\text{因為}M_it_i\equiv 1\pmod{m_i}\end{matrix}\ \right. \]

證畢

因為 \(m_i\) 不一定是質數,所以逆元使用拓展歐幾里得解決。

關於中國剩余定理的本質, 是構造出 \(R_i\) ,使得

\[R_i\equiv\left\{\begin{matrix}1&,&i=k\\ 0&,&i\neq k\end{matrix}\right.\pmod {m_k} \]

ll mul=1,M[N],m[N],a[N],t[N],ans;
ll exg0cd(ll a,ll b,ll& x,ll& y){
	if(!b){x=1,y=0 ;return a;}
	ll g = exgcd(b,a%b,x,y);
	ll t = x; x = y , y = t-a/b*x;
	return g;
}
void CRT(){
	ll b;
	for(int i = 1 ; i <= n ; i ++)
		M[i] = mul / m[i],
		exgcd(M[i],m[i],t[i],b),
		(ans += a[i]*M[i]%mul*t[i]%mul+mul) %= mul;
}
signed main(){
	n = read();
	for(int i = 1 ; i <= n ; i ++)
		m[i] = read(), a[i] = read(),
		mul *= m[i];
	CRT();
	printf("%lld",ans);
}

例題

同余方程

求關於 \(x\) 的同余方程 \(a x \equiv 1 \pmod {b}\) 的最小正整數解。

保證有解。

題解

來自前面的結論:當 \(\gcd(a,b)|1\) 時原方程一定有解。且有 \(\gcd(a,b)\) 個不同的解。

對於此題,將式子化為 \(ax+by=1\) ,即為求 \(ax+by=\gcd(a,b)\)\(x\) ,因為僅有一解,所以它就是最小整數解。

約數之和

\(A^B\) 的所有約數之和 \(\bmod9901\)

題解

\(A\) 的所有約數之和為 \(\prod \limits_{i=1}^m\begin{pmatrix}\sum\limits_{j=0}^{c_i} (p_i)^j\end{pmatrix}\)

那么 \(A^B\) 的所有約數之和為 \(\prod \limits_{i=1}^m\begin{pmatrix}\sum\limits_{j=0}^{\color{red}{B}c_i} (p_i)^j\end{pmatrix}\)

用等比數列展開一下,就是 \(\prod \limits_{i=1}^m\begin{pmatrix}\dfrac{p_i^{Bc_i+1}-1}{p_i-1}\end{pmatrix} \bmod 9901\)

對於累乘內的式子,我們要考慮它是否可以逆元求。

\(\gcd(p_i-1,9901)=1\) 時,可以直接用 費馬小定理 逆元求這一項。

否則,因為 \(9901\) 是質數,那么 \(9901|p_i-1\) 必定成立。

我們設 \(t=p_i-1\) , 則 \(p_i=t+1\)\(9901|t\)

原始相當於轉化為:求 \(\sum\limits_{j=0}^{Bc_i} (t+1)^j\bmod 9901\)

\((t+1)^j\) ,展開后對 \(9901\) 取模,答案一定為 \(1\) .

所以此項結果是 \(Bc_i+1\)

signed main(){	a = read(), b = read();	Prime();	for(int i = 1 ; i <= pcnt && a >= pri[i]; i ++){		if(!(a % pri[i])) p[++cnt] = pri[i];		while(!(a % pri[i])) c[cnt] ++, a /= pri[i];	}	if(a > 1) p[++cnt] = a, c[cnt] = 1;	ll ans = 1;	for(int i = 1 ; i <= cnt ; i ++)		if((p[i]-1) % mod) (ans *= (qpow(p[i],b*c[i]+1)-1) * qpow(p[i]-1,mod-2) % mod) %= mod;		else (ans += b*c[i]+1) %= mod;	printf("%lld",ans);}

合法序列

給你一個長度為 \(N\) 的正整數序列,如果一個連續的子序列,子序列的和能夠被 \(K\) 整除,那么就視此子序列合法,求原序列包括多少個合法的連續子序列?

題解

對於一個連續子序列 \([l,r]\) ,其和能被 \(K\) 整除,即為 \(sum_r \equiv sum_{l-1}\pmod K\)

用棧記錄即可。

	memset(stk,0,sizeof(stk));	stk[0] = 1;	k = read(), n = read();	for(int i = 1 ; i <= n ; i ++)		sum[i] = (sum[i-1] + read()) % k,		stk[sum[i]] ++;	ll ans = 0;	for(int i = 0 ; i < k ; i ++)		ans += (stk[i]-1)*stk[i] >> 1;	printf("%lld\n",ans);

青蛙的約會

有兩只青蛙,青蛙 A 和青蛙 B,他們在同一條經緯線上。他們將同時出發,沿着經緯線先西跳。規定緯度線上東經 \(0\) 度處為原點,由東往西為正方向,單位長度 \(1\) 米,這樣我們就得到了一條首尾相接的數軸。設青蛙A的出發點坐標是 \(x\) ,青蛙 B 的出發點坐標是 \(y\) 。青蛙 A 一次能跳 \(m\) 米,青蛙 B 一次能跳 \(n\) 米,兩只青蛙跳一次所花費的時間相同。緯度線總長 \(L\) 米。現在要你求出它們跳了幾次以后才會位於同一點。

題解

一道靈活運用同余方程的題。

首先顯然有 \(x+mt\equiv y+nt\pmod L\) .

移項一下,變成 \((m-n)t\equiv y-x\pmod L\) .

\(\Rightarrow a\times l +(m-n)t = y-x\) .

\(l\)\(m-n\) 看作常量,則原式是不定方程。

已知: \(ax+by\equiv c\) ,當且僅當 \(\gcd(a,b)| c\) 時有解。

所以原式先判斷是否有解。

若有解,則要求最小整數解。

根據同余方程的結論,若 \((x,y)\) 是原方程的解,那么 \((x\pm\dfrac b{\gcd(a,b)},y\mp\dfrac a{\gcd(a,b)})\) 也是解。

於是我們可以用這個方式得到原式的最小整數解。

ll exgcd(ll a,ll b,ll& x,ll& y){
	if(!b){x = 1 , y = 0; return a;}
	ll g = exgcd(b,a%b,x,y);
	ll t = x; x = y , y = t- a/b * x;
	return g;
}
signed main(){
	ll x = read(), y = read(), m = read(), n = read(), l = read();
	if(m < n) swap(n,m), swap(x,y);
	ll a,t;
	ll g = exgcd(l,m-n,a,t);
	if((y-x) % g) puts("Impossible");
	else printf("%lld",((t*(y-x)/g)%(l/g)+l/g)%(l/g));
}

總結

同余中有很多重要的結論與定理。需熟練掌握。

另外,同余方程與不定方程求解也是比較重要的內容。這方面仍需多加練習。







3.組合數學

概念

一、加法、乘法原理

加法原理:完成一個工程可以有 \(n\) 類辦法, \(a_i\) 代表第 \(i\) 類方法的數目。那么完成這件事共有 \(S=\sum\limits_{i=1}^n a_i\) 種不同的方法。

乘法原理:完成一個工程可以有 \(n\) 類辦法, \(a_i\) 代表第 \(i\) 類方法的數目。那么完成這件事共有 \(S=\prod\limits_{i=1}^n a_i\) 種不同的方法。

二、排列數與組合數

排列數:從 \(n\) 個不同元素中,任取 \(m\) 個元素按一定順序排成一列,用 \(\mathrm A_n^m\) 表示。

\[\mathrm A_n^m = n(n-1)(n-2)\cdots(n-m+1)=\dfrac{n!}{(n-m)!} \]

公式的理解:選取 \(m\) 個數,第 \(1\) 個數有 \(n\) 種可選,第 \(2\) 個數有 \((n-1)\) 種可選,……,第 \(m\) 個數有 \((n-m+1)\) 種可選,運用乘法原理得到公式。

全排列\(\mathrm A_n^m = n!\) ,表示 \(n\) 個數的全排列個數。

組合數: 從 \(n\) 個不同元素中,任取 \(m\) 個元素按組成一個集合。用 \(\mathrm C_n^m\) 表示。

\[\mathrm C_n^m=\dfrac {\mathrm A_n^m}{m!}=\dfrac{n!}{m!(n-m)!} \]

公式的理解:組成無序的集合,然后進行“全排列”,相當於排成一列。故 \(\mathrm C_n^m\times m!=\mathrm A_n^m\)

\(\mathrm C_n^m\) 還可以表示為 \(\begin{pmatrix}n\\m\end{pmatrix}\)

三、二項式定理

二項式定理闡明了一個展開式的系數:

\[(a+b)^n=\sum_{i=0}^n \begin{pmatrix}n\\i\end{pmatrix} a^ib^{n-i} \]

公式的理解:將 \((a+b)^n\) 拆開為 \((a+b)(a+b)\cdots(a+b)\) 。對於含有 \(a^i\) 的一項,相當於從 \(n\) 個因數中選取 \(i\) 個取 \(a\) ,有 \(\begin{pmatrix} n\\i\end{pmatrix}\) 種選取方式。另外,另 \(n-i\) 項必定選取 \(b\)

四、組合數的性質

\(\begin{pmatrix}n\\m\end{pmatrix}=\begin{pmatrix}n-m\\m\end{pmatrix}\)

\(n\) 中選 \(m\) 個,等價於從 \(n\) 中剩 \(n-m\) 個。

\(\begin{pmatrix}n\\m\end{pmatrix}=\dfrac nm\begin{pmatrix}n-1\\m-1\end{pmatrix}\)

\(\begin{pmatrix}n\\m\end{pmatrix}=\begin{pmatrix}n-1\\m-1\end{pmatrix}+\begin{pmatrix}n-1\\m\end{pmatrix}\)

遞推式,可以理解成:已知從 \(n-1\) 個數里選取的方案。考慮新加入一個數,若選取這個數,則相當於在原 \(n-1\) 個數中選取 \(m-1\) 個;若不選取這個數,則相當於在原 \(n-1\) 個數中選取 \(m\) 個。

五、盧卡斯定理

對於求組合數對質數取模,有:

\[\mathrm C_n^m\equiv \mathrm C_{n\ \bmod\ p}^{m\ \bmod\ p} \times\mathrm C_{\left\lfloor \tfrac np\right\rfloor}^{\left\lfloor \tfrac mp\right\rfloor} \pmod p \]

inline ll C(int n,int m){return n >= m ? fac[n]*ifac[m]%mod*ifac[n-m]%mod : 0;}
ll lucas(int n,int m){
	if(!m) return 1;
	return C(n%mod,m%mod)*lucas(n/mod,m/mod) % mod;
}

例題

古代豬文

iPig 在大肥豬學校圖書館中查閱資料,得知遠古時期豬文文字總個數為 \(n\) 。當然,一種語言如果字數很多,字典也相應會很大。當時的豬王國國王考慮到如果修一本字典,規模有可能遠遠超過康熙字典,花費的豬力、物力將難以估量。故考慮再三沒有進行這一項勞豬傷財之舉。當然,豬王國的文字后來隨着歷史變遷逐漸進行了簡化,去掉了一些不常用的字。

iPig 打算研究古時某個朝代的豬文文字。根據相關文獻記載,那個朝代流傳的豬文文字恰好為遠古時期的 \(\frac 1k\) ,其中 \(k\)\(n\) 的一個正約數(可以是 \(1\)\(n\))。不過具體是哪 \(\frac 1k\),以及 \(k\) 是多少,由於歷史過於久遠,已經無從考證了。

iPig 覺得只要符合文獻,每一種 \(k|n\) 都是有可能的。他打算考慮到所有可能的 \(k\) 。顯然當 \(k\) 等於某個定值時,該朝的豬文文字個數為 \(\frac nk\)。然而 \(n\) 個文字中保留下 \(\frac nk\) 個的情況也是相當多的。iPig 預計,如果所有可能的 \(k\) 的所有情況數加起來為 \(p\) 的話,那么他研究古代文字的代價將會是 \(g^p\)

現在他想知道豬王國研究古代文字的代價是多少。由於 iPig 覺得這個數字可能是天文數字,所以你只需要告訴他答案除以 \(999911659\) 的余數就可以了。

\(1\le n,g \le 10^9\) .

簡化題面

\(g^{\sum\limits_{i|n}\mathrm C^i_n}\pmod{999911659}\)

題解

數論大雜燴題啊……

分為以下步驟來進行:

  1. 根據歐拉定理的推論\(\begin{matrix}g^{ \sum\limits_{i|n}\mathrm C^i_n}\pmod{999911659} & = & g^{ \sum\limits_{i|n}\mathrm C^i_n\pmod{\varphi(999911659)}}\pmod{999911659} \\ & = & g^{ \sum\limits_{i|n}\mathrm C^i_n\pmod{999911658}}\pmod{999911659}\end{matrix}\) .
  2. 對於這個式子,我們可以先求 \sum\limits_{i|n}\mathrm C^i_n\pmod{999911658} $,再用快速冪來解決。
  3. 因為 \(n\leq 10^9\) ,枚舉其所有因數復雜度必定可行,所以先求出 \(n\) 的所有因數。
  4. 因為模數 \(999911658\) 不是質數,所以求 \(\mathrm C_n^i\) 不可以使用逆元
  5. 4. 的限制,我們不得考慮:
    • 將模數 \(999911658\) 分解質因數為 \(2\times3\times4679\times35617\) .
    • 分別計算 $$\mathrm C_n^i$$ 對於模上述的四個質數條件下的值。
    • 對於得到的四個值,等價於給定四個同余方程。
  6. 使用中國剩余定理來求這個確定的 $\sum\limits_{i|n}\mathrm C^i_n\pmod{999911658} $ .
ll n,g;
ll fac[N],ifac[N];
ll b[M] = {0,2,3,4679,35617};
ll qpow(ll a,int b,ll p){
    ll ret = 1;
    while(b){
        if(b & 1) (ret *= a) %= p;
        (a *= a) %= p, b >>= 1;
    }
    return ret;
}
ll a[M],m[M],t[M];
ll c[N],cnt;
inline ll C(int n,int m,ll p){return n >= m ? fac[n]*ifac[m]%p*ifac[n-m]%p : 0;}
ll lucas(int n,int m,ll p){
	if(!m) return 1;
	return C(n%p,m%p,p) * lucas(n/p,m/p,p) % p;
}

void work(int x){
    ll p = b[x];
    ifac[0] = fac[0] = 1;
    for(int i = 1 ; i < p ; i ++) fac[i] = fac[i-1] * i % p;
    ifac[p-1] = qpow(fac[p-1],p-2,p);
    for(int i = p-2 ; i ; i --) ifac[i] = ifac[i+1] * (i+1) % p;
    for(int i = 1 ; i <= cnt ; i ++) (a[x] += lucas(n,c[i],p)) %= p;
}
ll CRT(){
	ll mul = 1, ret = 0;
	for(int i = 1 ; i <= 4 ; i ++) mul *= b[i];
	for(int i = 1 ; i <= 4 ; i ++) m[i] = mul / b[i],
		t[i] = qpow(m[i],b[i]-2,b[i]);
    for(int i = 1 ; i <= 4 ; i ++)
		(ret += a[i]*m[i]*t[i]) %=  mul;
	return ret;
}
signed main(){
    n = read(), g = read();
    if(!(g % mod)) return puts("0"), 0;
    int ed = sqrt(n);
	for(int i = 1 ; i <= ed ; i ++)
		if(!(n % i))
			c[++cnt] = i,
			c[++cnt] = n/i;
	if(ed*ed == n) cnt --;
    for(int i = 1 ; i <= 4 ; i ++) work(i);
    ll ans = CRT();
    printf("%lld",qpow(g,ans,mod));
    return 0;
}

臨時筆記

Miller-Rabin素數

積性函數:單位函數、常數1、約數個數函數、約數和函數、歐拉函數

快速冪b=b%(p-1);

未完。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM