【原創】 中國剩餘定理 和 拓展中國剩餘定理

孫子

Preface

數論學習Part 7。
每天進步一點點,退役不會太丟臉。


聯賽只剩十五天,隔靴搔癢智何添?
剩餘定理四天一篇,動歸圖論又幾何,數據結構似雲煙。
莫再等閒,莫忘時間。

CRT

我們小時候都做過這種問題,一個數xx它滿足x{a1mod  p1a2mod  p2anmod  pnx\equiv \begin{cases}a_1 &\mod p_1 \\ a_2 &\mod p_2 \\ &\vdots \\ a_n & \mod p_n\end{cases},然後讓你求出一個最小的x出來。要求所有的pp互質。

遇到這種問題以前的我一般會各寫幾項出來,然後找相同。
這個時候孫子站了出來,他說出:記P=pP=\prod p,則x=k=1k=nakPpktkx=\sum_{k=1}^{k=n}a_k{\frac{P}{p_k}}t_ktkPpkpkt_k是\frac{P}{p_k}關於p_k的逆元。這是x的一個解,如果你逆元是最小正逆元,那這個x就是最小正解。

爲什麼呢?
證明啊……。

xa1mod  p1先看x\equiv a_1 \mod p_1。
Pp1p1t1使Pp1t11mod  p1,t1Pp1p1因爲\frac{P}{p_1}與p_1互質,所以\href{https://blog.csdn.net/c20182030/article/details/102631472#11_20}{肯定找得到數t_1使得\frac{P}{p_1}t_1\equiv 1\mod p_1},這個t_1被稱爲\frac{P}{p_1}關於p_1的逆元。
xa1a1×1a1Pp1p1mod  p1那麼就有x\equiv a_1\equiv a_1\times 1\equiv a_1\frac{P}{p_1}p_1\mod p_1。
Pp1=p2×p3××pna1Pp1p10(modp2),(modp3),,(modpn)因爲\frac{P}{p_1}=p_2\times p_3\times \cdots \times p_n,所以有a_1\frac{P}{p_1}p_1\equiv 0 \pmod {p_2},\pmod{p_3},\cdots,\pmod {p_n}。←不標準的表達,應當扣分
a2Pp2t2滿p2xp0以此類推,a_2\frac{P}{p_2}t_2就滿足模p_2時與x同餘,模其它p的時候得到0。
西s=k=1k=nakPpktk滿sa1+0×(n1)mod  p1那麼這些東西的和——s=\sum_{k=1}^{k=n}a_k{\frac{P}{p_k}}t_k,一定滿足s\equiv a_1+0\times(n-1)\mod p_1,sa2+0×(n1)mod  p2s\equiv a_2+0\times(n-1)\mod p_2,sa3+0×(n1)mod  p3s\equiv a_3+0\times(n-1)\mod p_3,\cdots,san+0×(n1)mod  pns\equiv a_n+0\times(n-1)\mod p_n,所以可以認爲ss是原方程的一個解。那麼通解就是s+zP,zZs+zP,z\in \Z

Q.E.D
就這?

代碼

洛谷板子題,雙倍經驗。

曹衝養豬

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
void Read(ll &p)
{
	p=0;
	int f=1;
	char c=getchar();
	while(c<'0' || c>'9') 
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9')
		p=p*10+c-'0',c=getchar();
	p*=f;
}

void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){d=a,x=1,y=0; return;}
	exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}

ll n,ans,d,x,y,P=1,a[1024],p[1024];

int main()
{
	Read(n);
	for(ll i=1;i<=n;i++) Read(p[i]),Read(a[i]),P*=p[i];
	for(ll i=1;i<=n;i++) exgcd(P/p[i],p[i],d,x,y),ans+=a[i]*(P/p[i])%p*x,((ans%=P)+=P)%=P;
	

[TJOI2009]猜數字

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
void Read(ll &p)
{
	p=0;
	int f=1;
	char c=getchar();
	while(c<'0' || c>'9') 
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9')
		p=p*10+c-'0',c=getchar();
	p*=f;
}

void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){d=a,x=1,y=0; return;}
	exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}

ll mul(ll a,ll b,ll p)
{
	return ((a*b-(ll)((long double)a/p*b+1e-8)*p)%p+p)%p;
}

ll n,ans,d,x,y,P,a[1024],p[1024];

int main()
{
	Read(n),P=1;
	for(ll i=1;i<=n;i++) Read(a[i]);
	for(ll i=1;i<=n;i++) Read(p[i]),((a[i]%=p[i])+=p[i])%=p[i],P*=p[i];
	for(ll i=1;i<=n;i++) exgcd(P/p[i],p[i],d,x,y),ans+=mul(mul(a[i],P/p[i],P),x,P),((ans%=P)+=P)%=P;
	printf("%lld\n",ans);
}

EXCRT

現在這些p不一定互質了。

我們先看頭兩個方程吼,{xa1mod  p1xa2mod  p2\begin{cases}x\equiv a_1\mod p_1\\x\equiv a_2 \mod p_2\end{cases}
那麼就有x=k1×p1+a1=k2×p2+a2k1×p1k2×p2=a2a1x=k_1\times p_1+a_1=k_2\times p_2+a_2\rArr k_1\times p_1-k_2\times p_2=a_2-a_1
gcd(p1,p2)=ggcd(p_1,p_2)=g,則同時除就有k1p1gk2p2g=a2a1g    k1p1ga2a1gmod  p2gk_1\frac{p_1}{g}-k_2\frac{p_2}{g}=\frac{a_2-a_1}{g}\iff k_1\frac{p_1}{g}\equiv \frac{a_2-a_1}{g}\mod {\frac{p_2}{g}},其中gcd(p1g,p2g)=1gcd(\frac{p_1}{g},\frac{p_2}{g})=1
注意,如果ga2a1g\nmid {a_2-a_1},則無解。
因爲這兩個數互質,所以可以求出逆元來,就有:k1a2a1g×(p1g)1mod  p2gk_1\equiv \frac{a_2-a_1}{g}\times (\frac{p_1}{g})^{-1}\mod \frac{p_2}{g}
所以設k1=q×p2g+a2a1g×(p1g)1k_1=q\times \frac{p_2}{g}+\frac{a_2-a_1}{g}\times (\frac{p_1}{g})^{-1}

知道k1k_1了,我們就知道了xx
x=k1×p1+a1=(q×p2g+a2a1g×(p1g)1)×p1+a1=q×p1p2g+a2a1g×(p1g)1×p1+a1x=k_1\times p_1+a_1=\left(q\times \frac{p_2}{g}+\frac{a_2-a_1}{g}\times (\frac{p_1}{g})^{-1}\right) \times p_1+a_1=q\times\frac{p_1p_2}{g}+\frac{a_2-a_1}{g}\times (\frac{p_1}{g})^{-1}\times p_1+a_1
    xa2a1g×(p1g)1×p1+a1mod  p1p2g\iff x\equiv\frac{a_2-a_1}{g}\times (\frac{p_1}{g})^{-1}\times p_1+a_1 \mod \frac{p_1p_2}{g}

我們把兩個同餘式合成了一個,太棒了。
看來我們可以做很多次,最後得到一個方程xAmod  Bx\equiv A\mod B
xx肯定可以隨便解了。

代碼

洛谷板子題,【模板】擴展中國剩餘定理(EXCRT)

居然一次過編譯過樣例,還直接A掉了。
我佛了。

太醜了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
void Read(ll &p)
{
	p=0;
	int f=1;
	char c=getchar();
	while(c<'0' || c>'9') 
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0' && c<='9')
		p=p*10+c-'0',c=getchar();
	p*=f;
}

ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){d=a,x=1,y=0; return;}
	exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}

ll mul(ll a,ll b,ll p)
{
	return ((a*b-(ll)((long double)a/p*b+1e-8)*p)%p+p)%p;
}

ll n,A,B,C,D,E,d,p,x,y;

int main()
{
	Read(n),n--,Read(B),Read(A);
	while(n--) Read(D),Read(C),d=gcd(B,D),exgcd(B/d,D/d,p,x,y),E=B/d*D,(A+=mul(mul((C-A)/d,x,E),B,E))%=E,B=E;
	printf("%lld\n",(A%B+B)%B);
}

CanKaoWenXian

CRT:其實是在我看了好幾篇舉個例子就說自己證好了的博客以後,再看到這篇博客的時候突然懂了,所以就以這篇博客爲參考文獻了。不過這篇文章確實給了我參考。


EXCRT
貞德真的好看。(不是貞德當我沒說)

說在後面

問了問數競的同學,他們的中國剩餘定理是證明其有解,然後數學歸納法就整出來了。
好吧,他們確實算不出來逆元。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章