「数论」数论题目选讲笔记
区间筛
-
给定区间
[L,R](L≤R≤2147483647,R-L≤1000000)
,请计算区间中素数的个数。- 原理 : 对于每一个合数 \(x\) , 都至少有一个质因子 \(\le \sqrt{x}\)
先筛出 [1,\(\sqrt{max\ int}\)] 内的质数 ,
再用 筛出的质数 对 \([L,R]\) 进行质数筛
code:
- 原理 : 对于每一个合数 \(x\) , 都至少有一个质因子 \(\le \sqrt{x}\)
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000010
int n, N, x, pri[10000];
typedef long long LL;
bool a[maxn];
int Eratosthenes_Sieve(int n, int pri[])
{
for (int i = 2; i * i <= n; i++)
if (a[i] == 0)
for (int j = i << 1; j <= n; j += i)
a[j] = 1;
int cnt = 0;
for (int i = 2; i <= n; i++)
if (!a[i])
pri[cnt++] = i;
return cnt;
}
int main()
{
int cnt = Eratosthenes_Sieve(50000, pri);
LL L, R;
scanf("%lld %lld", &L, &R);
memset(a, 0, sizeof(a));
for (int i = 0; i < cnt; i++)
for (LL j = max(2ll, (L - 1) / pri[i] + 1) * pri[i] ; j <= R; j += pri[i])
a[j - L] = 1;
int ans = 0;
for (LL i = L; i <= R; i++)
if (a[i - L] == 0)
ans++;
printf("%d", ans);
return 0;
}
$$\large https://www.luogu.org/problem/P3601$$
知识点: 简单数论, 欧拉函数, 筛法求素
题目要求 :
给定 \(l,r\) , 求 \(\large\sum \limits_{i=l}^{r} {i- \phi (i)}\)
\(1\le l\le r\le 1e12 \ ,\ r-l\le 1e6\)
分析题意 :
有下列性质 :
-
对于 \(x \in Z\), 有:
\(\large \phi(x) = x \times \prod\limits_{i=1}^{x}{\dfrac{p_i-1}{p_i}} (p_i \mid x)\)
\(x\) 的一质因子 \(p_i\) 对 \(\phi (x)\) 的贡献为 \(\dfrac{p_i-1}{p_i}\)
-
对于 一个合数\(x\) , 至多存在一个 质因子 , \(\ge \sqrt{x}\)
-
则可以筛出 \([1,\sqrt{1e12}]\) 内的质数
然后 用这些质数 对 \([l,r]\) 进行试除 ,若可整除 \(x\) , 则计算其对 \(\phi(x)\) 的贡献
-
如何特判 \(\ge \sqrt{x}\) 的 之多一个 质因子 \(p_{max}\)?
显然 , \(\large p_{max} = \dfrac{x}{\prod\limits p_i^{a_i}}\)则可 在试除时 , 对原数 \(x\) 进行操作 ,
全部试除完毕后 , 原数 \(x\) 变成 \(1\) 或者 \(p_{max}\)
此时再计算 \(p_{max}\) 的贡献即可
-
#include<cstdio>
#include<vector>
#include<ctype.h>
#define max(a,b) (a>b?a:b)
#define ll long long
const int MARX = 1e6+10;
const ll mod = 666623333;
//=============================================================
ll L,R,ans,K[MARX],phi[MARX];
bool flag[MARX];
std::vector <ll> prime;
//=============================================================
inline ll read()
{
ll s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
//=============================================================
signed main()
{
L = read(), R = read();
for(int i = 2; i < MARX; i ++)//筛出[2,1e6]中的质数
{
if(!flag[i]) prime.push_back(i);
for(int j = 2; i * j < MARX; j ++)
flag[i*j] = 1;
}
for(ll i = L; i <= R; i ++) phi[i-L] = K[i-L] = i;//初始化
for(int i = 0,size = prime.size(); i < size; i ++)//枚举质数
for(ll j = max(2ll, (L-1) / prime[i] + 1); prime[i]*j <= R; j ++)//枚举质数的倍数
{
ll x = prime[i] * j - L;
phi[x] = phi[x] / prime[i] * (prime[i]-1);//计算 贡献
for(;K[x] % prime[i] == 0;) K[x] /= prime[i];//除去 pi
}
for(ll i = L; i <= R; i ++)//枚举l~r中的数
{
if(K[i-L] != 1) phi[i-L] = phi[i-L] / K[i-L] * (K[i - L] - 1);//计算 >=sqrt(x)的质因子的贡献
ans = (ans + (i - phi[i-L])) % mod;//累加答案
}
printf("%lld", ans);
}
$$\large http://uoj.ac/problem/48$$
知识点: 简单数论 , 质因数分解
题目要求:
定义: \(sgcd(x,y)\) 为 \(x,y\) 的 次大 公约数 ,
\(ps\) : 当 \(\gcd(x,y) = 1\) 时 , \(sgcd(x,y) = -1\)
给定 一数列 \(a\) ,
求: \(sgcd(a_1,a_1),sgcd(a_1,a_2),\dots,sgcd(a_1,a_n)\)
\(n\le 1e5, a_i\le 1e12\)
分析题意:
-
有: \(\large x = \prod\limits{p_i^{a_i}}\ ,\ y = \prod\limits{p_i^{b_i}}\ ,\ \gcd{(x,y)} =\prod\limits{p_i^{\min(a_i,b_i)}}\)
因为 \(\large sgcd|x , sgcd|y\) ,
显然 , \(\large sgcd = \frac{\gcd}{\min(p_i)}\ (\text{满足 } p_i|x , p_i|y)\)则, 若已知 \(\gcd(a_1,a_i)\) 与 \(\min(p_i) \ (\text{满足 } p_i|x , p_i|y)\) ,
即可求得 \(sgcd(a_1,a_i)\) -
\(\gcd(a_1,a_i)\) 可在 \(\log(n)\) 时间内 求得 ,
如何求得 \(\min(p_i) \ (\text{满足 } p_i|x , p_i|y)\) ?-
发现 \(a_1\) 对所有的 答案 都作出 贡献.
则有 : \(p_i|a_1 \Leftrightarrow p_i|\gcd(a_1,a_i)\) -
则 可以 预处理出\(a_1\) 的所有质因子 \(p_i\) ,
并 按照升序 , 将所有 \(p_i\) 对 \(\gcd(a_1,a_i)\) 进行试除
则, 能够 整除\(\gcd(a_1,a_i)\)的 第一个\(p_i\) , 即为所求的 \(\min(p_i)\)
-
算法实现:
- 对 \(a_i\) 进行 质因数分解
- 枚举 每一个 \(a_i\)
- 求得 \(\gcd(a_1,a_i)\)
- 枚举 \(a_1\) 的 质因子 , 并对 \(\gcd(a_1,a_i)\) 进行试除
- 找到 \(\min(p_i) \ (\text{满足 } p_i|x , p_i|y)\)
计算 \(\large sgcd = \frac{\gcd}{\min(p_i)}\)
以上算法 , 极限复杂度为 \(\Theta(\sqrt{a_1} +n(\log{n}+k))\) ,
一般 跑不满 , 稳过 .
//知识点:数论 ,质因数分解
/*
By:Luckyblock
1A开心
分析题意:
*/
#include<cstdio>
#include<ctype.h>
#include<vector>
#define int long long
const int MARX = 1e5+10;
//=============================================================
int n,a1,ax;
std:: vector <int> prime;//质因数分解 a1
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
int gcd(int a,int b) {return b?gcd(b,a%b):a;}//求 最大公约数
void split(int x)//质因数分解
{
for(int i=2; i*i <=x; i++)
{
if(x%i == 0) prime.push_back(i);
while(x%i == 0) x /= i;
}
if(x != 1) prime.push_back(x);
}
//=============================================================
signed main()
{
n = read(); a1 = read();
split(a1);
printf("%lld ",a1/prime[0]);//sgcd(a1,a1)
for(int i=2; i<=n; i++)
{
ax = read();
int d = gcd(a1,ax);
if(d == 1) {printf("-1 "); continue;}//无sgcd
for(int j=0,size=prime.size(); j<size; j++)//枚举每一个 a1的 质因子
if(d % prime[j] == 0)//进行试除
{
printf("%lld ",d/prime[j]); //输出sgcd
break;
}
}
}
不定方程
$$\large https://www.luogu.com.cn/problem/P1445$$
知识点:约数个数, 唯一分解定理, 筛法求素
题目要求:
给定 \(\large n\) , 求 \(\large \dfrac{1}{x} + \dfrac{1}{y} = \dfrac{1}{n!}\) 的正整数解 的对数
分析题意:
-
首先对 求解式进行转化 :
\[\large \dfrac{1}{x} + \dfrac{1}{y} = \dfrac{1}{n!} \]\[\large y\times n!+x\times n! = x\times y \]\[\large x\times y - \large y\times n!-x\times n! = 0 \]\[\large x\times y - \large y\times n!-x\times n! + (n!)^2 = (n!)^2 \] -
根据 十字相乘法则 , 进行因式分解, 则有:
\[\large (x-n!)(y-n!) = n! \]由于 \(x,y,n \in Z\) , 则 \((x-n!) \text{与} (y-n!)\) 都为 \(n!\) 的约数
则 题目所求解的对数 , 即为 \(n!\) 的约数个数 -
根据 唯一分解定理 ,若设:
\[\large n! = \prod\limits{p_i^{c_i}}(p_i\mid n) \]根据约数和定理 , 则有:
\[\large \alpha(n!) = \prod\limits{(c_i+1)} \]则题目所求 即为:
\[\large \alpha((n!)^2) = \prod\limits{(2\times c_i+1)} \] -
由于 \(\large n!\) 的质因子都 \(\large\le n\) ,
则可以使用筛法直接预处理出所有 有贡献的素数 -
有一公式 :
对于 \(\large n!\) , \(\large p_i\) 在 \(\large n!\) 的质因数分解中 , 指数 的值 \(\large c_i\)
根据 此公式 , 即可可求得 指数 $c_i$ 的值
#include<cstdio>
#include<vector>
#include<ctype.h>
#define ll long long
const ll mod = 1e9+7;
const int MARX = 1e6+10;
//=============================================================
int n;
ll ans = 1;
bool flag[MARX];
std::vector <ll> prime;
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
//=============================================================
signed main()
{
n = read();
for(int i = 2; i <= n; i ++) //埃氏筛预处理所有 <=n的素数
{
if(!flag[i]) prime.push_back(i);
for(int j = 2; i * j <= n; j ++)
flag[i*j] = 1;
}
for(int i = 0,size = prime.size(); i < size && prime[i] <= n; i ++)//枚举素数
{
ll tmp = 0, x = n, y = prime[i];
for (; x; x /= prime[i]) tmp += x / y;//求得 x/y, x/(y^2), x/(y^3)...
ans = (ans * (tmp << 1 | 1) % mod) % mod;//累乘 计算贡献
}
printf("%lld",ans);
}
同余
$$\large https://www.luogu.org/problem/P2312$$
对于一个多项式 \(f(x)\) ,
\(f(x+p) \equiv f(x) \pmod{p}\)
则 若 \(f(x) = 0\), 有 \(f(x)\%p =0\)
可以使用 取模的方法 判断是否为 \(0\)