初等数论Ⅱ

By lby学长 2025.7.13 讲课记录 in smsky Summer Camp

目录

  • 大步小步算法(BSGS)
    • 例题
      • T1 [TJOI2007] 可爱的质数
      • T2 [SDOI2011] 计算器
      • T3 SPOJ3105 Mod
  • Stirling 数
    • 第二类 Stirling 数
    • 第一类 Stirling 数
    • Stirling 数 与 幂
    • 例题
      • T1 CF932E Team Work
      • T2 CF961G Partitions
      • T3 CF1278F Cards

大步小步算法(BSGS)

大步小步算法(Baby Step Giant Step) 用于求解如下问题:
求 t 满足 a t ≡ b ( m o d    p ) ,其中 p 是质数。 求 t 满足 a^t ≡ b (\mod p) ,其中 p 是质数。 t满足atb(modp),其中p是质数。

  • a , p a,p a,p 互质 时:
    根据 欧拉定理 可知: a ϕ ( p ) ≡ 1 ( m o d    p ) a^{\phi(p)} ≡1 (\mod p) aϕ(p)1(modp)
    于是便有: a t ≡ a t m o d    ϕ ( p ) ( m o d    p ) a^t≡ a^{t \mod \phi(p)} (\mod p) atatmodϕ(p)(modp)
    所以 t t t 的取值范围为 [ 0 , ϕ ( p ) − 1 ] [0,\phi(p)-1] [0,ϕ(p)1]
    考虑把 1 1 1, a a a, a 2 a^2 a2, …, a ϕ ( p ) − 1 a^{ϕ(p)−1} aϕ(p)1 依次排成一个圆,那么问题相当于问从起点要走几步能到达某个点。
    运用类似分块的思想。枚举要走多少大格和多少小步。设 t = k x − y t=kx-y t=kxy k = ϕ ( p ) k=\phi(p) k=ϕ(p) x ∈ [ 1 , ϕ ( p ) ] , y ∈ [ 0 , ϕ ( p ) − 1 ] x \in [1,\phi(p)] ,y \in [0,\phi(p)-1] x[1,ϕ(p)]y[0,ϕ(p)1]
    将原始代换得: a k x − y ≡ b ( m o d    p ) a^{kx-y}≡ b(\mod p) akxyb(modp)
    即为:
    a k x ≡ b × a y ( m o d    p ) a^{kx}≡ b \times a^y(\mod p) akxb×ay(modp)
    考虑将每个 y y y 对应的 b × a y b \times a^y b×ayHash表 存下来,枚举 x x x 找到最小的合法即可。
    当然代码时由于 ϕ ( p ) \phi(p) ϕ(p) 不方便求,进而以 p \sqrt{p} p 来代替 ϕ ( p ) \phi(p) ϕ(p) ,保证 p ≥ ϕ ( p ) \sqrt{p} \ge \phi(p) p ϕ(p)
unordered_map<ll,ll>mp;
ll BSGS(ll a,ll b,ll p){
	a%=p,b%=p;
	if(!a&&!b) return 1;
	if(b==1) return 0;
	if(__gcd(a,p)!=1) return -1;
	mp.clear();
	int siz=sqrt(p); if(siz*siz!=p) siz++;
	mp[b]=0; 
	ll bka=1;
	for(int i=1;i<=siz;i++){
		(b*=a)%=p; mp[b]=i;
		(bka*=a)%=p;
	}
	ll now=1,stp=0;
	for(int i=1;i<=siz;i++){
		(now*=bka)%=p;
		if(mp.count(now)) return 1ll*i*siz-mp[now];
	}
	return -1;
}
  • a , p a,p a,p 不互质 时:
    这就叫 扩展大步小步 。那就一直除 a t ≡ b ( m o d    p ) a^t ≡ b (\mod p) atb(modp) a a a p p p 的约数,直到互质为止。
    例如: 设 g = g c d ( a , p ) g=gcd(a,p) g=gcd(a,p)
    a t ≡ b ( m o d    p ) a^t ≡ b (\mod p) atb(modp)
    a g × a t − 1 ≡ b g ( m o d    p g ) \frac{a}{g} \times a^{t-1} ≡ \frac{b}{g} (\mod \frac{p}{g}) ga×at1gb(modgp)
    a t − 1 ≡ b g × ( a g ) − 1 ( m o d    p g ) a^{t-1} ≡ \frac{b}{g} \times (\frac{a}{g})^{-1}(\mod \frac{p}{g}) at1gb×(ga)1(modgp)
    可以发现,新的 a a a 不会变而 p p p 会,所以可能需要继续递归。如果 g ∤ b g \nmid b gb ,说明该方程无解。这个递归套用 Exgcd 即可。
void Exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){
		x=1,y=0;
		return;
	}
	Exgcd(b,a%b,y,x); y-=a/b*x;
}

unordered_map<ll,ll>mp;
ll BSGS(ll a,ll b,ll p){
	mp.clear();
	int siz=sqrt(p); if(siz*siz!=p) siz++;
	mp[b]=0; 
	ll bka=1;
	for(int i=1;i<=siz;i++){
		(b*=a)%=p; mp[b]=i;
		(bka*=a)%=p;
	}
	ll now=1;
	for(int i=1;i<=siz;i++){
		(now*=bka)%=p;
		if(mp.count(now)) return 1ll*i*siz-mp[now];
	}
	return -1;
}

ll ExBSGS(ll a,ll b,ll p){
	a%=p,b%=p;
	if(b==1||p==1) return 0;
	if(!a){
		if(!b) return 1;
		return -1;
	}
	ll gcd=__gcd(a,p),stp=0,ad=1;
	while(gcd!=1){
		if(b%gcd!=0) return -1;
		stp++,b/=gcd,p/=gcd,(ad*=a/gcd)%=p;
		gcd=__gcd(a,p);
		if(ad==b) return stp;
	}
	ll x=0,y=0; Exgcd(ad,p,x,y); x%=p,(x+=p)%=p;//!
	ad=x*b%p;
	ll stp2=BSGS(a,ad,p);
	if(stp2==-1) return -1;
	return stp+stp2;
}

例题

T1 [TJOI2007] 可爱的质数

link
直接套用普通 BSGS 模板即可。

T2 [SDOI2011] 计算器

link
T1

T3 SPOJ3105 Mod

link
板子 exBSGS


Stirling 数

常出现在 组合计数 中。

第二类 Stirling 数

n n n 个区分的球装入 m m m 个不区分的盒子,要求盒子非空,记为 S 2 ( n , m ) S2(n, m) S2(n,m) { n m } {n\brace m} {mn}
考虑第 n n n 个球,因为盒子不区分,可以新开一个盒子单独放,也可以插入到之前的一个盒子,那么:
S 2 ( n , m ) = S 2 ( n − 1 , m − 1 ) + m × S 2 ( n − 1 , m ) \rm S2(n, m) = S2(n−1, m−1) + m \times S2(n−1, m) S2(n,m)=S2(n1,m1)+m×S2(n1,m)
这个显然是 O ( n m ) O(nm) O(nm) 的。
当然还可以归纳出通项公式,可以通过容斥推出
S 2 ( n , m ) = ∑ i = 0 m ( − 1 ) m − i × i n ( m − i ) ! i ! \rm S2(n, m) = \sum\limits_{i=0}^{m}(-1)^{m-i} \times \frac{i^n}{(m-i)!i!} S2(n,m)=i=0m(1)mi×(mi)!i!in
注意只有 S 2 ( 0 , 0 ) = 1 S2(0,0)=1 S2(0,0)=1 ,其余的 S 2 ( i , 0 ) = 0 S2(i,0)=0 S2(i,0)=0。!!!

第一类 Stirling 数

n n n 个区分的球装入 m m m 个不区分的盒子,要求盒子非空且盒子内形成圆排列,记为 S 1 ( n , m ) S1(n, m) S1(n,m) [ n m ] {n\brack m} [mn]
考虑第 n n n 个球,同样可以新开一个盒子,也可以插入之前的盒子,但是此时盒子内是圆排列,所以插入到某个盒子开头和末尾是等价的,就是说有 n − 1 n−1 n1 种插法:
S 1 ( n , m ) = S 1 ( n − 1 , m − 1 ) + ( n − 1 ) × S 1 ( n − 1 , m ) S1(n, m) = S1(n−1, m−1) + (n − 1) \times S1(n−1,m) S1(n,m)=S1(n1,m1)+(n1)×S1(n1,m)
注意第一类斯特林数只有 O ( n m ) O(nm) O(nm) 递推式!

Stirling 数 与 幂

m n = ∑ i = 0 n S 2 ( n , i ) × m i ‾ m^n=\sum\limits_{i=0}^{n} S2(n,i) \times m^{\underline{i}} mn=i=0nS2(n,i)×mi
其中: m i ‾ = C m i × i ! m^{\underline{i}}=C_m^i \times i! mi=Cmi×i!

解释: m n m^n mn 的意义为 n n n 个区分的球装入 m m m 个区分的盒子,那么先用 C m i C_m^i Cmi 选出有 i i i 个盒子非空(剩下 m − i m−i mi 个空盒子),乘上 S 2 ( n , i ) S2(n, i) S2(n,i) 将球不区分地装入,最后乘上 i ! i! i! 把盒子区分。

例题

T1 CF932E Team Work

link
题意
求: ∑ i = 1 n ( n i ) i k \sum\limits_{i=1}^{n} \tbinom{n}{i} i^k i=1n(in)ik
1 ≤ n ≤ 1 0 9 , 1 ≤ k ≤ 5000 1 \le n \le 10^9,1 \le k \le 5000 1n109,1k5000

思路
原式 = ∑ i = 1 n ( n i ) ∑ j = 1 k { k j } i j ‾ = ∑ j = 1 k { k j } j ! ∑ i = 1 n ( n i ) ( i j ) = ∑ j = 1 k { k j } j ! ∑ i = 1 n ( n j ) ( n − j i − j ) = ∑ j = 1 k { k j } j ! ( n j ) ∑ i = 0 n − j ( n − j i ) = ∑ j = 1 k { k j } j ! ( n j ) 2 n − j \begin{aligned} 原式&= \sum\limits_{i=1}^{n} \binom{n}{i} \sum\limits_{j=1}^{k} {k\brace j} i^{\underline{j}}\\ &= \sum\limits_{j=1}^{k}{k\brace j}j!\sum\limits_{i=1}^{n} \binom{n}{i} \binom{i}{j}\\ &= \sum\limits_{j=1}^{k}{k\brace j}j!\sum\limits_{i=1}^{n} \binom{n}{j} \binom{n-j}{i-j} \\ &=\sum\limits_{j=1}^{k}{k\brace j}j! \binom{n}{j} \sum\limits_{i=0}^{n-j} \binom{n-j}{i}\\ &=\sum\limits_{j=1}^{k}{k\brace j}j! \binom{n}{j} 2^{n-j} \end{aligned} 原式=i=1n(in)j=1k{jk}ij=j=1k{jk}j!i=1n(in)(ji)=j=1k{jk}j!i=1n(jn)(ijnj)=j=1k{jk}j!(jn)i=0nj(inj)=j=1k{jk}j!(jn)2nj

代码

#pragma GCC optimize(3)
#include
#define ll long long
using namespace std;
const int maxk=5005,mod=1e9+7;

ll Pow_(ll x,ll y){
	ll s=1;
	while(y){
		if(y&1) (s*=x)%=mod;
		(x*=x)%=mod;
		y>>=1;
	}
	return s;
}

ll sl[maxk][maxk];
int main(){
	int n,k; cin>>n>>k;
	sl[0][0]=1;
	for(int i=1;i<=k;i++)
		for(int j=1;j<=i;j++)
			sl[i][j]=(sl[i-1][j-1]+j*sl[i-1][j]%mod)%mod;
	ll ans=0,tmp=1,pw2=Pow_(2,n),inv2=Pow_(2,mod-2);
	for(int i=1;i<=k;i++){
		(tmp*=(n-i+1))%=mod;
		(pw2*=inv2)%=mod;
		(ans+=sl[k][i]*tmp%mod*pw2%mod)%=mod;
	}	
	cout<<ans;
	return 0;
}

T2 CF961G Partitions

link
题意
求: ∑ j = 1 n w j ∑ i = 1 n i ( n − 1 i − 1 ) { n − i k − 1 } \sum\limits_{j=1}^{n} w_j \sum\limits_{i=1}^{n} i\binom{n-1}{i-1} {n-i\brace k-1} j=1nwji=1ni(i1n1){k1ni}
思路
∑ i = 1 n i ( n − 1 i − 1 ) { n − i k − 1 } = ∑ i = 1 n i ( n − 1 i − 1 ) ∑ j = 0 k − 1 ( − 1 ) k − 1 − j j n − i ( k − 1 − j ) ! j ! = ∑ j = 0 k − 1 ( − 1 ) k − 1 − j ( k − 1 − j ) ! j ! ∑ i = 1 n i ( n − 1 i − 1 ) j n − i \begin{aligned} \sum\limits_{i=1}^{n} i\binom{n-1}{i-1} {n-i\brace k-1}&=\sum\limits_{i=1}^{n} i\binom{n-1}{i-1} \sum\limits_{j=0}^{k-1}(-1)^{k-1-j}\frac{j^{n-i}}{(k-1-j)!j!}\\ &=\sum\limits_{j=0}^{k-1}\frac{(-1)^{k-1-j}}{(k-1-j)!j!}\sum\limits_{i=1}^{n} i\binom{n-1}{i-1} j^{n-i} \end{aligned} i=1ni(i1n1){k1ni}=i=1ni(i1n1)j=0k1(1)k1j(k1j)!j!jni=j=0k1(k1j)!j!(1)k1ji=1ni(i1n1)jni
考虑如何快速求解 i i i 这层循环:配平二项式,因为若没有 × i \times i ×i ,此式就为二项式反演了。
∑ i = 1 n i ( n − 1 i − 1 ) j n − i = ∑ i = 1 n ( i − 1 ) ( n − 1 i − 1 ) j n − i + ∑ i = 1 n ( n − 1 i − 1 ) j n − i = ( n − 1 ) ∑ i = 1 n ( n − 2 i − 2 ) j n − i + ( j + 1 ) n − 1 = ( n − 1 ) ( j + 1 ) n − 2 + ( j + 1 ) n − 1 = ( j + 1 ) n − 2 ( n + j ) \begin{aligned} \sum\limits_{i=1}^{n} i\binom{n-1}{i-1} j^{n-i} &=\sum\limits_{i=1}^{n} (i-1)\binom{n-1}{i-1} j^{n-i}+\sum\limits_{i=1}^{n} \binom{n-1}{i-1} j^{n-i}\\ &=(n-1) \sum\limits_{i=1}^{n} \binom{n-2}{i-2} j^{n-i}+(j+1)^{n-1}\\ &=(n-1)(j+1)^{n-2}+(j+1)^{n-1}\\ &=(j+1)^{n-2}(n+j) \end{aligned} i=1ni(i1n1)jni=i=1n(i1)(i1n1)jni+i=1n(i1n1)jni=(n1)i=1n(i2n2)jni+(j+1)n1=(n1)(j+1)n2+(j+1)n1=(j+1)n2(n+j)
所以原式变成了:
∑ j = 0 k − 1 ( − 1 ) k − 1 − j ( k − 1 − j ) ! j ! ( j + 1 ) n − 2 ( n + j ) \sum\limits_{j=0}^{k-1}\frac{(-1)^{k-1-j}}{(k-1-j)!j!}(j+1)^{n-2}(n+j) j=0k1(k1j)!j!(1)k1j(j+1)n2(n+j)
代码

#include
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;

ll Pow_(ll x,ll y){
	ll s=1;
	while(y){
		if(y&1) (s*=x)%=mod;
		(x*=x)%=mod;
		y>>=1;
	}
	return s;
}

ll a[maxn],fac[maxn],inv_fac[maxn];
int main(){
	int n,k; cin>>n>>k;
	fac[0]=inv_fac[0]=1;
	for(int i=1;i<=k;i++){
		fac[i]=fac[i-1]*i%mod;
		inv_fac[i]=Pow_(fac[i],mod-2);
	}
	ll sum=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		(sum+=a[i])%=mod;
	}
	if(n==1){
		if(k==1) cout<<a[1];
		else cout<<0;
		return 0;
	}
	ll ans=0;
	for(int i=0;i<k;i++){
		if((k-1-i)&1) ans-=Pow_(i+1,n-2)*(n+i)%mod*inv_fac[k-1-i]%mod*inv_fac[i]%mod,(ans+=mod)%=mod;
		else (ans+=Pow_(i+1,n-2)*(n+i)%mod*inv_fac[k-1-i]%mod*inv_fac[i]%mod)%=mod;
	}
	cout<<sum*ans%mod;
	return 0;
}

T3 CF1278F Cards

link
题意

你可能感兴趣的:(学习笔记,数论)