2025 暑假集训 Day14

2025.8.19 简单数论

知识点梳理

注:本文中 \(p\) 为质数,\([P]\) :如果命题 \(P\) 成立,\([P]=1\) 否则 \([P]=0\).

整数唯一分解定理:

\[n=\prod_{i=1}^m p_i^{c_i} \]

以下代码可以求出这个式子的 \(p_i,c_i,m\)

ll p[N],c[N];  //找出n的质因子和次数 
int m=0;
inline void calc(ll n)
{
	for(ll i=2;i*i<=n;i++)
	{
		if(n%i==0)
		{
			p[++m]=i;
			for(;n%i==0;n/=i) c[m]++;
		}
	}
	if(n!=1) p[++m]=n,c[m]=1;
}

推论:若 \(n\) 已经被分解为 \(n=\prod_{i=1}^m p_i^{c_i}\),则:

  1. \(n\) 的正约数集合为:\(\left\{p_1^{b_1}p_2^{b_2} \cdots p_m^{b_m}\right\}\),其中 \(0 \le b_i \le c_i\)
  2. \(n\) 的正约数个数为:\(\prod_{i=1}^m(c_i+1)\)
  3. \(n\) 的所有正约数和:\(\prod_{i=1}^m \sum_{j=0}^{c_i} p_i^j\).

素数筛法

埃氏筛:可以在 \(O(n \ln \ln n)\) 的复杂度内筛出 \(1 \sim n\) 的质数,vis 数组标记这个数是不是质数,如果是就是 falsea 数组就是筛出来的数。

bitset<N> vis;
int a[N];
int n,idx=0;
inline void sieve()
{
	vis[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			a[idx++]=i;
			for(int j=i+i;j<=n;j+=i) vis[j]=1; 
		}
	}
}

欧拉筛(线性):高贵的线性 \(O(n)\) 复杂度:

bitset<N> vis;
int p[N],idx;
int n;
void sieve()
{
	vis[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) p[idx++]=i;
		for(int j=0;j<idx&&p[j]*i<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
}

GCD 和 LCM

  1. \(a|m,b|m\),则 \(\operatorname{lcm}(a,b)|m\)
  2. \(m|a,m|b\),则 \(m|\gcd(a,b)\)
  3. \(a,b,m \in {\mathbf N^+}\),则 \(\operatorname{lcm}(ma,mb)=m \operatorname{lcm}(a,b)\)(“提取”出了一个 \(m\)
  4. \(gcd(a,b)\times\operatorname{lcm}(a,b)=a \times b\)
  5. (辗转相除法)\(\gcd(a,b)=\gcd(b,a \bmod b)\)
  6. (更相减损术)\(\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)

欧拉函数

定义:\(\varphi(n)\) 表示 \([1,n]\) 中与 \(n\) 互质的数的个数,即:

\[\varphi(n)=\sum_{i=1}^n[\gcd(n,i)=1] \]

如果 \(n\) 已经被分解成整数唯一分解定理 \(n=\prod_{i=1}^m p_i^{c_i}\) 的形式,可以用如下式子求值:

\[\varphi (n)=n \prod_{p|n}(1-\dfrac{1}{p}) \]

欧拉函数的性质:

  1. \(\varphi(1)=1\)
  2. \(n\) 为质数时,\(\varphi(n)=n-1\)
  3. \(p\) 为质数时,对于 \(n=p^k\),有 \(\varphi(n)=p^k-p^{k-1}=(p-1)p^{k-1}\)
  4. (积性函数的性质)对于所有互质整数 \(a,b\),有 \(\varphi (ab)=\varphi(a) \times \varphi(b)\)
  5. 对于质数 \(p\),若 \(p|n\)\(\varphi(np)=\varphi(n) \times p\),否则 \(\varphi(n)=\varphi(n) \times \varphi(p-1)\)
  6. \(\forall n>1\)\(1 \sim n\) 中与 \(n\) 互质的数的和为 \(\dfrac{1}{2}n\varphi(n)\),即 \(\sum_{i=1}^n[\gcd(i,n)=1]i=\dfrac{1}{2}n\varphi(n)\)
  7. \(\sum_{d|n}\varphi(d)=n\)

欧拉筛可以在筛质数的时候同时把 \(\varphi(n)\) 算出来,时间复杂度 \(O(n)\)

int n,phi[N],p[N],idx=0;
bitset<N> vis;
inline void init_phi(int val)
{
	vis[1]=1;
	phi[1]=1;
	for(int i=2;i<=val;i++)
	{
		if(!vis[i])
		{
			p[idx++]=i;
			phi[i]=i-1;
		}
		for(int j=0;j<idx&&i*p[j]<=val;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0)
			{
				phi[i*p[j]]=phi[i]*p[j];  //运用欧拉函数的性质5
				break;
			}
			else phi[i*p[j]]=phi[i]*phi[p[j]];  //运用欧拉函数的性质4(积性函数)
		}
	}
}

题目

Sumdiv

题目描述 给定两个自然数 $A,B \le 5 \times 10^7$。设 $S$ 为 $A^B$ 的所有自然数因子之和。计算 $S \bmod 9901$ 的结果。

原题:POJ 1845

考虑使用整数唯一分解定理:

\[A=\prod_{i=1}^m p_i^{c_i} \]

则:

\[A^B=\prod_{i=1}^m p_i^{c_iB} \]

由上文所述整数唯一分解定理的推论 3,有 \(A^B\) 的所有自然数因子的和为:

\[(1+p_1+p_1^2+\cdots+p_1^{c_1B})(1+p_2+p_2^2+\cdots+p_2^{c_2B})\cdots(1+p_m+p_m^2+\cdots+p_m^{c_mB}) \]

发现每一个括号是一个首项为 \(1\),公比为 \(p_i\),末项为 \(p_i^{c_iB}\) 的等比数列,根据等比数列求和公式 \(S_n=\dfrac{a_n-a_nq}{1-q}\),上式可以化为:

\[\prod_{i=1}^m \dfrac{p_1^{c_iB+1}-1}{p_i-1} \]

然后就可以 \(O(m)\) 求值了,分母部分使用乘法逆元就可以了。

注意:如果分母 \(p_i-1 \bmod 9901=0\) 就不能使用乘法逆元(因为分母不能为 \(0\),取模之后等于 \(0\) 就无意义了),重新把那一个括号里面的式子算一遍:因为 \(p_i-1 \bmod 9901=0\),所以:

\[p_i \equiv 1 \mod 9901 \]

\[1+p_1+\cdots+p_i^{c_iB} \equiv 1+1+\cdots+1=c_iB+1 \mod 9901 \]

代码 https://vjudge.net/solution/63039611

#include<iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=50000007;
const ll mod=9901;
inline ll ksm(ll a,ll b)
{
	ll s=1;
	while(b)
	{
		if(b&1) s=s*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return s%mod;
}
ll p[N],c[N];  //找出a的质因子和次数 
int m=0;
inline void calc(ll n)
{
	for(ll i=2;i*i<=n;i++)
	{
		if(n%i==0)
		{
			p[++m]=i;
			for(;n%i==0;n/=i) c[m]++;
		}
	}
	if(n!=1) p[++m]=n,c[m]=1;
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	ll a,b,ans=1;
	cin>>a>>b;
	calc(a);
	if(a==0) return cout<<1,0;
//	for(int i=1;i<=m;i++) cerr<<p[i]<<' '<<c[i]<<endl;
	for(int i=1;i<=m;i++)
	{
		if((p[i]-1)%mod==0) ans=(ans*(c[i]*b%mod+1))%mod;
		else ans=(ans*(ksm(p[i],c[i]*b+1)+mod-1)*ksm(p[i]-1,mod-2)%mod)%mod;
	}
	cout<<ans;
	return 0;
}
/*
答案是
$$\prod _{i=1}^m \dfrac{p_i^{c_iB+1}-1}{p_i-1}$$ 
*/

洛谷 P5253 [JSOI2013] 丢番图

https://www.luogu.com.cn/problem/P5253

首先 \(\dfrac{1}{x}+\dfrac{1}{y}=\dfrac{1}{n}\) 这东西可以通过同分等操作得到以下式子:

\[xy-n(x+y)=0 \]

然后可以发现这个东西距离可以使用式子相乘法因式分解还差一个二次项,于是就等式两边同时加上一个 \(n^2\),然后因式分解得到:

\[(n-x)(n-y)=n^2 \]

然后就尝试把 \(n^2\) 分成 \(n^2=pq\) 的形式,可以分出多少个 \((p,q)\)?答案就是 \(\left \lceil \dfrac{n^2 的约数个数}{2} \right \rceil\)

用整数唯一分解一下然后就可以线性时间复杂度搞定了。

https://www.luogu.com.cn/record/231939930

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=-1;
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	ll n,ans=1,c=1;
	cin>>n;
	for(ll i=2;i*i<=n;i++)
	{
		c=1;
		while(n%i==0)
		{
			n/=i;
			c+=2;
		}
		ans*=c;
	}
	if(n!=1) ans*=3;
	cout<<(ans+1)/2;
	return 0;
}
/*
1/x+1/y=1/n
(n-x)(n-y)=n^2
n^2=1*n^2=..*..=..*..
可以分成几对因数的乘积?ceil(n^2的约数个数/2) 
n^2=prod(i=1~m)p[i]^(2c[i])的约数个数是(2c[1]+1)*...*(2c[m]+1) O(m)搞定 
*/

UVA12716 GCD等于XOR GCD XOR

https://www.luogu.com.cn/problem/UVA12716

\(a \oplus b\) 表示 \(a,b\) 按位异或的值。

\(a = b\) 时,\(a \oplus b = 0\),但是 \(\gcd(a, b)\) 不可能 \(=0\),所以 \(a \ne b\)。又因为 \(a \ge b\),所以 \(a > b\)

\(\gcd(a, b) = \gcd(b, a - b) \le a - b\),所以有不等式 1:\(\gcd(a, b) \le a - b\)

不等式 2:\(a \oplus b \ge a - b\)(因为 \(\oplus\) 相当于二进制下的不退位减法)。

\(\gcd(a, b) = a \oplus b\) 时,上面两个不等式同时取到等号。

所以 \(\gcd(a, b) = a \oplus b\) 当且仅当 \(\gcd(a, b) = a - b\)\(a \oplus b = a - b\)

现在问题就变成了统计 \(a \oplus b = a - b\)\((a, b)\) 个数。

\(\gcd(a, b) = a - b = a \oplus b = k\)

\(a = a'k\)\(b = b'k\),则 \(\gcd(a'k, b'k) = (a' - b')k\),且 \(a'\)\(b'\) 互质。

\(a - b = k\),即 \(a = b + k\),且 \(a\)\(b\) 都是 \(k\) 的倍数。然后我们可以考虑枚举 \(k,b\),预处理答案。

开一个数组 \(s_i\) 预处理 \(1 \le a \le i\)\(a-b = a \oplus b\) 的个数,然后就可以 \(O(1)\) 查询了。

https://www.luogu.com.cn/record/231977064

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=3e7+7;
int s[N];  //s[i]表示1<=a<=i中a-b=a xor b的个数 
int n;
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	for(int k=1;k<=30000000;k++)
	{
		for(int b=k;b+k<=30000000;b+=k)
		{
			int a=b+k;
			if((a^b)==(a-b)) s[a]++;
		}
	}
	for(int i=1;i<=30000000;i++) s[i]=s[i-1]+s[i];  //差分数组变前缀和数组 
	int T,a;
	scanf("%d",&T);
	for(int i=1;i<=T;i++)
	{
		scanf("%d",&a);
		printf("Case %d: %d\n",i,s[a]);
	}
	return 0;
}
/*
当a=b时a xor b=0,但是gcd(a,b)不可能=0,所以a!=b又因为a>=b所以a>b
gcd(a,b)=gcd(b,a-b)<=a-b 所以有不等式1:gcd(a,b)<=a-b 
不等式2:a xor b>=a-b(因为xor相当于(二进制下的)不退位减法) 
当gcd(a,b)=a xor b时,上面两个不等式同时取到等号
所以gcd(a,b)=a xor b当且仅当gcd(a,b)=a-b,a xor b=a-b
现在问题就变成了统计a xor b=a-b的(a,b)个数
gcd(a,b)=a-b=a xor b=k
(a',b'互质)gcd(a'k,b'k)=a'k-b'k=(a'-b')k
a-b=k,a=b+k
a,b都是k的倍数 
*/

P2158 [SDOI2008] 仪仗队

https://www.luogu.com.cn/problem/P2158

假设 C 君站的位置为 \((0,0)\) 的话。

如果位置 \((x,y)\) 能够被看到,那么坐标 \((kx,ky),k \in {\mathbf{N}}^+\) 也会被看到(因为在一条直线上),所以题目本质上是让你统计 \(x,y\) 互质的 \((x,y)\) 个数(\(x,y\) 不同时为 \(1\))。

然后可以把整个正方形沿着对角线 \(y=x\) 分成两半,先计算左上半个数,发现就是一个 \(\sum_{i=1}^{n-1}\varphi(i)\),右下半和左上半的答案是一样的。别忘了加上对角线 \(y=x\) 上的一个。

答案就是:

\[2 \cdot \left( \sum_{x=1}^{n-1} \sum_{y=1}^{x} [\gcd(x, y) = 1] \right) - 1 = 2 \cdot \left( \sum_{x=1}^{n-1} \varphi(x) - 1 \right) + 1 = 2 \cdot \sum_{x=1}^{n-1} \varphi(x) + 1 \]

posted @ 2025-08-19 22:12  wwwidk1234  阅读(60)  评论(0)    收藏  举报