zoukankan      html  css  js  c++  java
  • 数论初步

    一些定理

    裴蜀定理
    若关于 (x,y) 的不定方程 (ax+by=c) 有解((a,b,c in mathbf{Z})),则 (c mod gcd(a,b) =0)
    费马小定理
    (p) 为质数,且 (gcd(a,p)=1) ,则 (a^{p-1} equiv 1 pmod{p})
    欧拉定理
    (gcd(a,m)=1) ,则 (a^{varphi(m)} equiv 1 pmod{m})
    扩展欧拉定理
    (a^b equiv egin{cases} a^{b mod varphi(p)}, & gcd(a,p)=1 \ a^b, & gcd(a,p) eq 1,b<varphi(p) \ a^{b mod varphi(p)+varphi(p)}, & gcd(a,p) eq 1, b geq varphi(p) end{cases} pmod p)

    exgcd

    求关于 (x)(y) 的方程 (ax+by=gcd(a,b)) 的一组解。
    (ax_1+by_1=gcd(a,b))(bx_2+(a mod b)y_2=gcd(b,a mod b))
    因为 (gcd(a,b)=gcd(b,a mod b))
    所以 (ax_1+by_1=bx_2+(a mod b)y_2)
    所以 (ax_1+by_1=bx_2+(a-lfloor frac{a}{b} floor cdot b)y_2)
    (bx_2+(a-lfloor frac{a}{b} floor cdot b)y_2=ay_2+b(x_2-lfloor frac{a}{b} floor cdot y_2))
    所以 (ax_1+by_1=ay_2+b(x_2-lfloor frac{a}{b} floor cdot y_2))
    所以 (x_1=y_2)(y_1=x_2-lfloor frac{a}{b} floor cdot y_2)
    注意到当 (b=0) 时令 (x=1) 即可得到解,以其为边界递归即可。

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

    CRT

    求解如下形式的方程组(其中,(m_1,m_2,cdots,m_k) 两两互质):
    (egin{cases} x & equiv & a_1 pmod{m_1} \ x & equiv & a_2 pmod{m_2} \ & vdots \ x & equiv & a_k pmod{m_k} \ end{cases})
    (M=prod limits_{i=1}^k m_i)(w_i=frac{M}{m_i})(w_i^{-1})(w_i) 在模 (m_i) 意义下的逆元。
    (x_{min}=sum limits_{i=1}^k a_iw_iw_i^{-1} mod M)

    for(int i=1;i<=k;i++)
    {
    	int w=M/m[i];
    	int x=0,y=0;
    	exgcd(w,m[i],x,y);
    	if(x<0) x+=m[i];
    	ans+=a[i]*w*x%M,ans%=M;
    }
    

    exCRT

    考虑上面的方程组中模数不互质的情况。
    设当前求解到第 (i) 个方程,前 (i-1) 个方程的最小解为 (x)(operatorname{lcm}(m_1,m_2,cdots,m_k)=M)
    则前 (i-1) 个方程的通解为 (x+tM)
    那么,现在要找到一个 (t) ,使 (x+tM equiv a_i pmod{m_i})
    这个式子显然是可以使用 exgcd 求解的。
    用裴蜀定理判一下无解。

    for(int i=1;i<=n;i++)
    {
    	int res=(a[i]-X%m[i]+m[i])%m[i];
    	int x=0,y=0;
    	int d=exgcd(M,m[i],x,y);
    	if(res%d) puts("-1"),exit(0);
    	X+=x*res/d*M;//算上倍数,更新答案
    	M*=m[i]/d,X+=M,X%=M;
    }
    

    Lucas

    对于质数 (p) ,有 (displaystyle inom{n}{m} equiv displaystyle inom{lfloor frac{n}{p} floor}{lfloor frac{m}{p} floor} cdot displaystyle inom{n mod p}{m mod p} pmod p)
    在模数不大且为质数时,可以使用它来递归算组合数。

    int lucas(int n,int m,int p)
    {
    	if(!m) return 1;
    	return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;
    }
    

    代码中, C 函数为暴力算组合数。

    exLucas

    算模数非质数时的组合数。
    先将模数 (p) 质因数分解,设 (p=prod limits_{i=1}^k q_i^{a_i}) (其中 (q_i) 为质数)。
    可以构造出下面的同余方程组:
    (egin{cases} displaystyle inom{n}{m} & equiv & b_1 pmod{q_1^{a_1}} \ displaystyle inom{n}{m} & equiv & b_2 pmod{q_2^{a_2}} \ & vdots \ displaystyle inom{n}{m} & equiv & b_k pmod{q_k^{a_k}} \ end{cases})
    因为这个同余方程组的所有模数都互质,所以只要求出 (b_{1 cdots k}) 即可用 CRT 求出 (displaystyle inom{n}{m})
    现在,问题转化为求 (displaystyle inom{n}{m} mod p^k) 的值((p) 为质数) ,即 (frac{n!}{m!(n-m)!} mod p^k) 的值。
    但是,分母在模 (p^k) 意义下不一定有乘法逆元,所以要再进行一些转化。
    (n!) 中包含 (x)(p) 因子, (m!) 中包含 (y)(p) 因子, ((n-m)!) 中包含 (z)(p) 因子,
    则原式可以转化为 (p^{x-y-z}dfrac{dfrac{n!}{p^x}}{dfrac{m!}{p^y}dfrac{(n-m)!}{p^z}} mod p^k)
    现在,问题又转化为求 (frac{n!}{p^x} mod p^k) 的值 ((p) 为质数)。
    (n!) 进行转化:
    首先,把 (n!) 中所有为 (p) 的倍数的乘数和非 (p) 的倍数的乘数分开,(n!=(p imes 2p imes 3p imes cdots)(1 imes 2 imes 3 imes cdots))
    因为 (1) ~ (n) 中有 (lfloor frac{n}{p} floor)(p) 的倍数,所以进一步拆开,得 (p^{lfloor frac{n}{p} floor}(lfloor frac{n}{p} floor)!prod limits_{i=1,i mod p eq 0}^n i)
    因为模数为 (p^k) ,所以再把后半个式子每 (p^k) 个分成一组,得 (p^{lfloor frac{n}{p} floor}(lfloor frac{n}{p} floor)!(prod limits_{i=1,i mod p eq 0}^{p^k} i)^{lfloor frac{n}{p^k} floor}(prod limits_{i=p^k imes lfloor frac{n}{p^k} floor, i mod p eq 0}^{n} i))
    因为还要除以 (p^x) ,所以 (p^{lfloor frac{n}{p} floor}) 一定没有了,但是, ((lfloor frac{n}{p} floor)!) 中为 (p) 的倍数的乘数也要除去。
    (f(x)=frac{n!}{p^x}) ,其中 (x) 同样代表 (n!) 中包含 (p) 因子的数目,
    (f(n)=f(lfloor frac{n}{p} floor)(prod limits_{i=1,i mod p eq 0}^{p^k} i)^{lfloor frac{n}{p^k} floor}(prod limits_{i=p^k imes lfloor frac{n}{p^k} floor, i mod p eq 0}^{n} i)) ,递归计算即可。
    代回原式中,发现还有 (p^{x-y-z}) 无法计算,考虑如何计算 (x,y,z)
    下面以 (frac{n!}{p^x}) 中的 (x) 为例。
    (g(x)=) 上面的 (x)
    观察前面化出的阶乘的式子,即可得到 (g(n)=g(lfloor frac{n}{p} floor)+lfloor frac{n}{p} floor) ,同样递归计算即可。

    int f(int x,int p,int P)//P=p^k
    {
    	if(!x) return 1;
    	int sum=1;
    	for(int i=1;i<=P;i++)
    		if(i%p) sum*=i,sum%=P;
    	sum=POW(sum,x/P,P);
    	for(int i=x/P*P;i<=x;i++)
    		if(i%p) sum*=i%P,sum%=P;
    	return f(x/p,p,P)*sum%P;
    }
    int g(int x,int p)
    {
    	if(!x) return 0;
    	return g(x/p,p)+x/p;
    }
    int C(int n,int m,int p,int P)
    {
    	return f(n,p,P)*inv(f(m,p,P),P)%P*inv(f(n-m,p,P),P)%P*POW(p,g(n,p)-g(m,p)-g(n-m,p),P)%P;
    }
    

    代码中, POW 为快速幂函数,inv 为求逆元函数。

    BSGS

    求解方程 (a^x equiv b pmod p) ,其中 (a,p) 互质。
    (x=At-B)(t) 为定值),
    (a^{At-B} equiv b pmod p)
    因为 (a,p) 互质,所以 (a^{At} equiv ba^B pmod p)
    可以枚举 (B) 的取值,存进 Hash 表中。
    再枚举 (A) 的取值,判断 Hash 表中是否存在这个值,若存在,则找到了一个 (x) 的取值。
    (t=lceil sqrt p ceil) 时复杂度最优。
    代码用 map 实现。

    map <int,int> mp;
    int t=ceil(sqrt(P));
    int sum=b,s=1;
    mp[sum]=0;
    for(int i=1;i<=t;i++) sum*=a,sum%=P,s*=a,s%=P,mp[sum]=i;
    sum=s;
    for(int i=1;i<=t;i++)
    {
    	if(mp.count(sum)) printf("%lld",i*t-mp[sum]),exit(0);
    	sum*=s,sum%=P;
    }
    puts("-1");
    

    exBSGS

    同样求解上面的方程,但是 (a,p) 不保证互质。
    (a,p) 不互质时,不存在 (a) 在模 (p) 意义下的逆元,也就没有上面的推导。
    考虑如何让 (a,p) 互质。
    (gcd(a,p)=d_1) ,方程两边同除以 (d_1) ,得到 (frac{a}{d_1} cdot a^{x-1} equiv frac{b}{d_1} pmod{frac{p}{d_1}})
    若当前还没有满足 (a) 与模数互质,则继续设 (gcd(a,frac{p}{d_1})=d_2) ,方程两边同除以 (d_2) ,得到 (frac{a}{d_1d_2} cdot a^{x-1} equiv frac{b}{d_1d_2} pmod{frac{p}{d_1d_2}})
    重复这样的操作,直到 (a) 与模数互质。
    设进行了 (k) 次操作,每次操作时得到的 (d) 的乘积为 (D)
    则当前方程转化为了 (frac{a^k}{D} cdot a^{x-k} equiv frac{b}{D} pmod{frac{p}{D}})
    此时,就可以用普通的 BSGS 求解了。
    注意,每次操作过后要用裴蜀定理判一下无解,还要特判 (x leq k) 的情况。

    int BSGS(int x)
    {
    	mp.clear();
    	int t=ceil(sqrt(P));
    	int sum=b,s=1;
    	mp[sum]=0;
    	for(int i=1;i<=t;i++) sum*=a,sum%=P,s*=a,s%=P,mp[sum]=i;
    	sum=x*s%P;
    	for(int i=1;i<=t;i++)
    	{
    		if(mp.count(sum)) return i*t-mp[sum];
    		sum*=s,sum%=P;
    	}
    	return -1;
    }
    int exBSGS()
    {
    	if(b==1||p==1) return 0;
    	int k=0,sum=1;
    	while(1)
    	{
    		int d=gcd(a,P);
    		if(d==1) break;
    		if(b%d) return -1;
    		k++,b/=d,P/=d,sum*=a/d,sum%=P;
    		if(sum==b) return k;
    	}
    	int ans=BSGS(sum);
    	return (ans!=-1)*k+ans;
    }
    

    原根

    :设 (a,p) 为两个正整数,且 (gcd(a,p)=1) ,则使 (a^x equiv 1 pmod p) 成立的最小正整数 (x) ,即为 (a)(p) 的阶,记作 ( ext{ord}_p a)
    (g,a) 为两个正整数,且 (gcd(g,a)=1) ,若 ( ext{ord}_a g=varphi (a)) ,则 (g)(a) 的一个原根。
    若一个数有原根,则它一定可以表示为 (2,4,p^k,2p^k) 的形式((p) 为奇质数,(k) 为任意正整数)。
    (g)(a) 的原根,则对于任意 (varphi(a)) 的质因子 (p) ,都有 (g^{frac{varphi(a)}{p}} ot equiv 1 pmod a)
    (g)(a) 的最小原根,则集合 (S={g^k | 1 leq k leq varphi(a),gcd(k,varphi(a))=1}) 中的元素均为 (a) 的原根。
    因此,一个数 (a)(varphi(varphi(a))) 个原根,且这些原根模 (a) 两两不同余。
    同时,若一个数 (a) 存在原根,则它的最小原根是不大于 (a^{frac{1}{4}}) 级别的。所以求最小原根时可以直接枚举。

    int find(int x)
    {
    	int tot=0;
    	int X=phi[x];
    	for(int i=2;i*i<=X;i++)
    		if(X%i==0)
    		{
    			a[++tot]=i;
    			while(X%i==0) X/=i;
    		}
    	if(X>1) a[++tot]=X;
    	for(int i=1;;i++)
    	{
    		bool r=true;
    		if(POW(i,phi[x],x)!=1) r=false;//原根的定义 
    		for(int j=1;j<=tot;j++)
    			if(POW(i,phi[x]/a[j],x)==1)
    			{
    				r=false;
    				break;
    			}
    		if(r) return i;
    	}
    }
    void get(int x)
    {
    	int t=find(x),s=1;
    	for(int i=1;i<=phi[x];i++)
    	{
    		s*=t,s%=x;
    		if(gcd(i,phi[x])==1) ans[++sum]=s;
    	}
    }
    
  • 相关阅读:
    java实现还款计算
    java实现风险度量
    java实现字符串比较
    java实现风险度量
    java实现风险度量
    java实现还款计算
    java实现还款计算
    java实现字符串比较
    java实现字符串比较
    java实现风险度量
  • 原文地址:https://www.cnblogs.com/zhs1/p/14397416.html
Copyright © 2011-2022 走看看