素数计数函数:小于或等于 x x x 的素数个数,用 π ( x ) \pi(x) π(x) 表示。随着 x x x 的增大,有这样近似的结果: π ( x ) ∼ x ln x \pi(x) \sim \frac{x}{\ln x} π(x)∼lnxx
这个方法好,不用开根号,也不用担心溢出了。复杂度仍是 O ( n ) O(\sqrt n) O(n)
bool is_prime(int x) {
if (x < 2) return false;
for (int i = 2; i <= x / i; i++) {
if (x % i == 0) return false;
}
return true;
}
输入多个数字,判断有多少个数是质数,输入数字范围是 n < 2 31 n < 2^{31} n<231
对数 n n n 进行 k k k 轮测试的时间复杂度是 O ( k log 3 n ) O(k \log^3n) O(klog3n)
因为 1 1 1 是 p p p 的二次剩余,若 p p p 是奇素数, x 2 ≡ 1 ( m o d p ) x^2 \equiv 1(\mod p) x2≡1(modp) 必有两根 1 , p − 1 1,p-1 1,p−1. 因此 a = q ∗ 2 p a = q*2^p a=q∗2p,若 a q ≠ 1 a ^{q} \ne 1 aq=1,那么必然先遇到 − 1 -1 −1 再遇到 1 1 1 ,否则与 x 2 ≡ 1 ( m o d p ) x^2 \equiv 1(\mod p) x2≡1(modp) 有两根相矛盾.
i64 mod_pow(i64 x, i64 n, i64 mod)
{
i64 res = 1;
while(n)
{
if(n & 1) res = mul(res, x, mod);
x = mul(x, x, mod);
n >>= 1;
}
return res;
}
bool millerRabin(i64 n)
{
if(n == 1) return false;
if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) return true;
if(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0 || n % 11 == 0 || n % 13 == 0) return false;
i64 a = n - 1;
int b = 0;
while(a % 2 == 0) a /= 2, b++;
i64 test[7] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022LL};
for(int i = 0, j; i < 7 && test[i] < n; i++)
{
i64 x = test[i], v = mod_pow(x % mod, a, n);
if(v == 1) continue;
for(j = 0; j < b; j++)
{
if(v == n - 1) break;
v = mul(v, v, n);
}
if(j >= b) return false;
}
return true;
}
void prime_factor(int x, map<int, int>& res) {
for (int i = 2; i <= x / i; i++) {
while (x % i == 0) {
res[i]++;
x /= i;
}
}
//分解质因数,这一行千万别少,也别写成 res.empty() !!!
if (x != 1) res[x]++;
}
可以先筛掉 n \sqrt n n 范围内的素数,然后用深搜分解质因数. 这里提供一个暴力分解求质因子幂次之和的模板
#define x first
#define y second
int get_div(int n, int id)
{
auto it = mp.find(n);
if(it != mp.end()) return it->y;
if(n % prime[id] == 0)
{
return mp[n] = get_div(n / prime[id], id) + 1;
}
else if(prime[id + 1] * prime[id + 1] <= n) return mp[n] = get_div(n, id + 1);
return mp[n] = n == 1 ? 0 : 1;
}
st 数组保存最小素因数
int prime[maxn], cnt, st[maxn];
void sieve(int N) {
for (int i = 2; i <= N; i++) {
if (!st[i]) st[i] = prime[cnt++] = i;
for (int j = 0; prime[j] <= N / i; j++) {
st[prime[j] * i] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
vector<int> divisor(int N) {
vector<int> res;
for (int i = N; i > 1; ) {
//找到 j 的最小质因数
int x = st[i];
res.push_back(x);
//把j一直除以最小质因数,直到不能被 x 整除为止。
while (i % x == 0) {
i /= x;
}
}
return res;
}
int prime[maxn];
bool is_prime[maxn + 1];
int sieve(int n) {
int p = 0;
for (int i = 0; i <= n; i++) is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
prime[p++] = i;
for (int j = 2 * i; j <= n; j += i) is_prime[j] = false;
}
}
return p;
}
int prime[maxn], cnt;
bool st[maxn];
void get_primes(int N) {
for (int i = 2; i <= N; i++) {
if (!st[i]) prime[cnt++] = i;
for (int j = 0; prime[j] <= N / i; j++) { //枚举到i的最小质因子的时候就停下来。
st[prime[j] * i] = true;
if (i % prime[j] == 0) break; //pj 是 i 的最小质因子。
}
}
}
196. 质数距离
//prime1保存[2, sqrt(b)]素数, prime2保存[a, b]之间的素数. maxn1是sqrt(b)的最大值, maxn2是b - a的最大值。st1保存保存[2, sqrt(b)]是否是素数, prime2保存[a, b]是否为素数。
int prime1[maxn1], cnt1;
bool st1[maxn1], st2[maxn2];
ll prime2[maxn2], cnt2;
//筛[2, sqrt(b)]素数
void sieve(int N) {
for (int i = 2; i <= N; i++) {
if (!st1[i]) prime1[cnt1++] = i;
for (int j = 0; prime1[j] <= N / i; j++) {
st1[prime1[j] * i] = true;
if (i % prime1[j] == 0) break;
}
}
}
//筛[a, b]之间的素数.
void segment_sieve(ll a, ll b) {
memset(st2, 0, sizeof st2);
cnt2 = 0;
for (int i = 0; i < cnt1; i++) {
ll p = prime1[i];
for (ll j = max(2 * p, (a + p - 1) / p * p); j <= b; j += p) {
st2[j - a] = true;
}
}
for (int i = 0; i <= b - a; i++) {
if (!st2[i] && a + i >= 2) prime2[cnt2++] = a + i;
}
}
HDU 2973 YAPTCHA (威尔逊定理及其逆定理)
vector<int> divisor(int n) {
vector<int> res;
for (int i = 1; i <= n / i; i++) {
if (n % i == 0) {
res.push_back(i);
if (i != n / i) res.push_back(n / i);
}
}
sort(res.begin(), res.end());
return res;
}
ll divisor(int x) {
unordered_map<int, int> res;
for (int i = 2; i <= x / i; i++) {
while (x % i == 0) {
res[i]++;
x /= i;
}
}
if (x != 1) res[x]++;
ll ans = 1;
for (auto p : res) ans = ans * (p.second + 1) % mod;
return ans;
}
int nums[maxn]
for(int i = 1; i <= N; i++){
for(int j = i; j <= N; j += i){
nums[j]++;
}
}
ll ans = 1;
for (auto prime : res) {
int p = prime.first, a = prime.second;
ll t = 1;
//小心这里,是循环a次。想想为什么。
for (int i = 0; i < a; i++) {
t = (t * p + 1) % mod;
}
ans = ans * t % mod;
}
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
#include
using namespace std;
int f(int n, int m){
int ans = 1;//算上本身那种情况
if (n == 1) return 0;
for (int i = m; i <= n / i; i++){
//从2开始遍历找所有的能分解的情况
if (n % i == 0){
//上面相当于把子问题漏掉的那种情况加上了
ans += f(n / i, i);
//把子问题的所有情况也加上
//因为 a = a1 * a2 * a3 * ... * an,并且1 < a1 <= a2 <= a3 <= ... <= an,
//因为后面的因数要比前面大,漏了这一个
}
}
return ans;
}
int main(){
int n;
cin >> n;
for (int i = 1; i <= n; i++){
int a;
cin >> a;
int ans = f(a, 2);
cout << ans << endl;
}
return 0;
}
i64 Pollard_Rho(i64 x)
{
//找到 x 的一个因子
i64 s = 0, t = 0;
i64 c = (i64)(rnd_64()) % (x - 1) + 1;
int step = 0, goal = 1;
i64 val = 1;
for (goal = 1;; goal *= 2, s = t, val = 1)
{
//倍增优化
for (step = 1; step <= goal; ++step)
{
t = ((i128)t * t + c) % x;
val = (i128)val * abs(t - s) % x;
if ((step % 127) == 0)
{
i64 d = __gcd(val, x);
if (d > 1) return d;
}
}
i64 d = __gcd(val, x);
if (d > 1) return d;
}
}
#include
#include
using namespace std;
typedef long long ll;
ll N, K;
int main() {
scanf("%lld%lld", &N, &K);
ll ans = N * K;
for (ll l = 1, r; l <= N; l = r + 1) {
if (K / l == 0) break;
r = min(K / (K / l), N);
ans -= (K / l) * (l + r) * (r - l + 1) / 2;
}
printf("%lld\n", ans);
}
若 p p p 为素数, g c d ( a , p ) = 1 gcd(a, p) = 1 gcd(a,p)=1,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(\mod p) ap−1≡1(modp).
#include
typedef long long ll;
ll mod_pow(ll x, ll n, ll mod) {
ll res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
ll a, p;
scanf("%lld%lld", &a, &p);
ll ans = mod_pow(a, p - 2, p);
if (a % p) printf("%lld\n", ans);
else printf("impossible\n");
}
return 0;
}
int N;
void solve() {
int ans = N;
for (int i = 2; i <= N / i; i++) {
if (N % i == 0) {
ans = ans / i * (i - 1);
while (N % i == 0) N /= i;
}
}
if (N > 1) ans = ans / N * (N - 1);
printf("%d\n", ans);
}
int prime[maxn], phi[maxn], cnt;
bool st[maxn];
void get_eulers(int N) {
phi[1] = 1;
for (int i = 2; i <= N; i++) {
if (!st[i]) {
prime[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; prime[j] <= N / i; j++) {
st[i * prime[j]] = true;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
$$
a^b \equiv \begin{cases}a^{b\mod \varphi§}, & \gcd(a,p) = 1\
a^b, & \gcd(a, p) \ne 1, b < \varphi§ \
a^{b \mod \varphi§ + \varphi§}, & \gcd(a, p) \ne 1, b \ge \varphi§
\end{cases}
$$
#include
int exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
#include
typedef long long ll;
int exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int a, b, m, x, y;
scanf("%d%d%d", &a, &b, &m);
int d = exgcd(a, m, x, y);
if (b % d) printf("impossible\n");
else printf("%lld\n", (ll)x * (b / d) % m);
}
return 0;
}
#include
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll& x, ll& y)
{
if(!b){
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int T;
scanf("%d", &T);
while(T--){
//求 a 在模 b 意义下的逆元
ll a, b, x, y;
scanf("%lld%lld", &a, &b);
exgcd(a, b, x, y);
x = (x % b + b) % b;
if(x) printf("%lld\n", x);
else printf("impossible\n");
}
return 0;
}
看清m和a,和上面的定理不太照应。而且公式一定要看清,超级超级容易弄混。
设两个方程分别是 x ≡ m 1 ( m o d a 1 ) x\equiv m_1 \pmod {a_1} x≡m1(moda1)、 x ≡ m 2 ( m o d a 2 ) x\equiv m_2 \pmod {a_2} x≡m2(moda2);
将它们转化为不定方程: x = a 1 p + m 1 = a 2 q + m 2 x=a_1p+m_1=a_2q+m_2 x=a1p+m1=a2q+m2,其中 p , q p, q p,q 是整数,则有 a 1 p − a 2 q = m 2 − m 1 a_1p-a_2q=m_2-m_1 a1p−a2q=m2−m1。
由裴蜀定理,当 m 2 − m 1 m_2-m_1 m2−m1 不能被 gcd ( a 1 , a 2 ) \gcd(a_1,a_2) gcd(a1,a2) 整除时,无解;
其他情况下,可以通过扩展欧几里得算法解出来一组可行解 ( p , q ) (p, q) (p,q);
则原来的两方程组成的模方程组的解为 x ≡ M ( m o d A ) x\equiv M\pmod A x≡M(modA),其中 M = a 1 p + m 1 M=a_1p+m_1 M=a1p+m1, A = lcm ( a 1 , a 2 ) A=\text{lcm}(a_1, a_2) A=lcm(a1,a2)。
#include
#include
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int N;
scanf("%d", &N);
ll a1, m1;
bool ok = true;
scanf("%lld%lld", &a1, &m1);
for (int i = 1; i < N; i++) {
ll a2, m2, k1, k2;
scanf("%lld%lld", &a2, &m2);
//k1 * a1 - k2 * a2 = m2 - m1, 求出 k1 和 k2 的值。
ll d = exgcd(a1, a2, k1, k2);
if ((m2 - m1) % d) {
ok = false;
break;
}
//k1 = k0 + a2 / d * k,将k1扩大m2 - m1倍,再想办法让k1是最小正整数。
k1 *= (m2 - m1) / d;
ll t = a2 / d;
k1 = (k1 % t + t) % t; //这里可以将k1变成最小正整数。
//x = k0 * a1 + m1 + [a1, a2];
m1 = a1 * k1 + m1;
a1 = abs(a1 / d * a2);
}
if (ok) printf("%lld\n", (m1 % a1 + a1) % a1);
else printf("-1\n");
return 0;
}
定义在 N ∗ \N^* N∗ 上的函数 f f f: N ∗ → A \N^* \rightarrow A N∗→A 都可以称作是数论函数,其中 A A A 可以是由加减乘运算的集合.
一些常见的数论函数
单位函数 ε ( n ) = { 1 , n = 1 0 , o t h e r w i s e \varepsilon(n) = \begin{cases}1,n=1 \\ 0,otherwise\end{cases} ε(n)={1,n=10,otherwise
幂函数 I d k ( n ) = n k . Id_k(n) = n^k. Idk(n)=nk. 当 k = 1 k=1 k=1 时为恒等函数 I d ( n ) Id(n) Id(n),当 k = 0 k = 0 k=0 时为常数函数 1 ( n ) 1(n) 1(n)
除数函数 σ k ( n ) = ∑ d ∣ n d k \sigma_k(n) = \sum_{d \mid n}d^k σk(n)=∑d∣ndk,当 k = 1 k=1 k=1 时为因数和函数 σ ( n ) \sigma(n) σ(n),当 k = 0 k=0 k=0 时为因数个数函数 σ 0 ( n ) \sigma_0(n) σ0(n). 也写作 τ ( n ) = ∑ d ∣ n 1 \tau (n) = \sum_{d \mid n}1 τ(n)=∑d∣n1,表示正整数 n n n 的正因子个数.
数论函数 f f f 叫做是积性函数,如果对于任意两个互素的正整数 n n n 和 m m m ,都满足 f ( n m ) = f ( n ) f ( m ) f(nm) = f(n)f(m) f(nm)=f(n)f(m).
若 n = p 1 a 1 p 2 a 2 . . . p k a k n=p_1^{a_1}p_2^{a_2}...p_k^{a_k} n=p1a1p2a2...pkak,则有
f ( n ) = f ( p 1 a 1 ) f ( p 2 a 2 ) . . . f ( p k a k ) = ∏ i = 1 k f ( p i a i ) . f(n) = f(p_1^{a_1})f(p_2^{a_2})...f(p_k^{a_k}) = \prod\limits_{i=1}^{k}f(p_i^{a_i}). f(n)=f(p1a1)f(p2a2)...f(pkak)=i=1∏kf(piai).
狄利克雷卷积:对于数论函数 f f f 和 g g g,它们的卷积表示成 f ∗ g f*g f∗g,卷积的结果是一个数论函数 h h h,且
( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) = ∑ a b = n f ( a ) g ( b ) . (f*g)(n) = \sum\limits_{d \mid n}f(d)g(\frac{n}{d}) = \sum\limits_{ab=n}f(a)g(b). (f∗g)(n)=d∣n∑f(d)g(dn)=ab=n∑f(a)g(b).
若 f , g f,g f,g 都是积性函数,那么 f ∗ g f*g f∗g 也是积性函数
证明
( f ∗ g ) ( a ) ⋅ ( f ∗ g ) ( b ) = ( f ∗ g ) ( a b ) . (f*g)(a) \cdot (f*g)(b) = (f*g)(ab). (f∗g)(a)⋅(f∗g)(b)=(f∗g)(ab).
狄利克雷卷积的一些性质 证明
交换律: f ∗ g = g ∗ f f*g=g*f f∗g=g∗f
结合律: ( f ∗ g ) ∗ h = f ∗ ( g ∗ h ) (f * g) * h=f * (g * h) (f∗g)∗h=f∗(g∗h)
分配律: ( f + g ) ∗ h = f ∗ h + g ∗ h (f + g) * h = f * h + g * h (f+g)∗h=f∗h+g∗h.
等式的性质: f = g f=g f=g 的充要条件是 f ∗ h = g ∗ h f*h = g*h f∗h=g∗h,其中数论函数 h ( x ) h(x) h(x) 要满足 h ( 1 ) ≠ 0 h(1) \ne 0 h(1)=0.
单位元:即单位函数 ε ( n ) = { 1 , n = 1 0 , o t h e r w i s e \varepsilon(n) = \begin{cases}1,n=1 \\ 0,otherwise\end{cases} ε(n)={1,n=10,otherwise
假设 f ∗ g = ε f*g=\varepsilon f∗g=ε,则称 g g g 是 f f f 的狄利克雷逆元,记作 f − 1 f^{-1} f−1. f f f 有逆元的必要条件是 f ( 1 ) ≠ 0 f(1) \ne 0 f(1)=0.
f − 1 ( n ) = { 1 f ( n ) , n = 1 − 1 f ( 1 ) ∑ d ∣ 1 , d > 1 f ( d ) f − 1 ( n d ) f^{-1}(n) = \begin{cases} \frac{1}{f(n)},n=1 \\ -\frac{1}{f(1)} \sum\limits_{d\mid 1, d>1}f(d)f^{-1}(\frac{n}{d}) \end{cases} f−1(n)=⎩ ⎨ ⎧f(n)1,n=1−f(1)1d∣1,d>1∑f(d)f−1(dn)
实际上,从抽象代数角度看,取狄利克雷卷积为乘法,普通函数加法为加法,则数论函数集构成一个整环。注意它不构成一个域,因为并不是每个非零元素(这里的零元是 0 ( x ) : = 0 0(x):=0 0(x):=0)都有逆元,而必须要满足 f ( 1 ) ≠ 0 f(1) \ne 0 f(1)=0.
需要指出,积性函数必然存在逆元(因为 f ( 1 ) = 1 ≠ 0 f(1) = 1 \ne 0 f(1)=1=0 ),且逆元仍是积性函数。
函数之间的关系 证明
除数函数和幂函数: ( I d k ∗ 1 ) ( n ) = ∑ d ∣ n I d k ( d ) = ∑ d ∣ n d k (Id_k*1)(n) = \sum\limits_{d \mid n}Id_k(d) = \sum\limits_{d \mid n}d^k (Idk∗1)(n)=d∣n∑Idk(d)=d∣n∑dk,即 I d k ∗ 1 = σ k Id_k * 1 = \sigma_k Idk∗1=σk
欧拉函数与恒等函数: ( φ ∗ 1 ) ( n ) = n (\varphi * 1)(n) = n (φ∗1)(n)=n,该定理可以看作 n n n 的所有因数的欧拉函数和等于 n n n.
莫比乌斯函数和单位函数: ( μ ∗ 1 ) ( n ) = ∑ d ∣ n μ ( d ) = ε ( n ) = [ n = 1 ] (\mu * 1)(n) = \sum\limits_{d|n} \mu(d) = \varepsilon(n) = [n=1] (μ∗1)(n)=d∣n∑μ(d)=ε(n)=[n=1].
莫比乌斯函数就是看质因数分解后,质因子的个数是奇数还是偶数。
设 x = p 1 α 1 ∗ p 2 α 2 . . . p k α k x = p_1^{\alpha_1}*p_2^{\alpha_2}...p_k^{\alpha_k} x=p1α1∗p2α2...pkαk,则莫比乌斯函数定义为:
μ ( x ) = { 0 , ∃ α i > 1 1 , k 为偶数 − 1 , k 为奇数 \mu(x)=\begin{cases}0,\ \exist \alpha_i>1\\1,\ k为偶数\\-1,\ k为奇数\end{cases} μ(x)=⎩ ⎨ ⎧0, ∃αi>11, k为偶数−1, k为奇数
莫比乌斯函数有如下性质:
∑ d ∣ n μ ( d ) = { 1 , n = 1 0 , n ≠ 1 \sum\limits_{d \mid n} \mu(d) = \begin{cases}1, n=1 \\ 0, n\ne 1 \end{cases} d∣n∑μ(d)={1,n=10,n=1
易证:设 n = p 1 α 1 ∗ p 2 α 2 . . . p k α k n = p_1^{\alpha_1}*p_2^{\alpha_2}...p_k^{\alpha_k} n=p1α1∗p2α2...pkαk,则 ∑ d ∣ n μ ( d ) = ( 1 − 1 ) k \sum\limits_{d \mid n} \mu(d) = (1-1)^k d∣n∑μ(d)=(1−1)k
又有 [ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i,j) = 1] = \sum\limits_{d \mid gcd(i,j)} \mu(d) [gcd(i,j)=1]=d∣gcd(i,j)∑μ(d).
#include
#include
#include
using namespace std;
const int maxn = 50010;
typedef long long ll;
int prime[maxn], cnt;
bool st[maxn];
int mobius[maxn], sum[maxn]; //莫比乌斯函数以及它的前缀和.
//线性筛法求莫比乌斯函数。
void sieve(int N) {
mobius[1] = 1;
for (int i = 2; i <= N; i++) {
if (!st[i]) {
prime[cnt++] = i;
mobius[i] = -1;
}
for (int j = 0; prime[j] <= N / i; j++) {
int t = prime[j] * i;
st[t] = true;
if (i % prime[j] == 0) {
mobius[t] = 0;
break;
}
mobius[t] = -mobius[i];
}
}
for (int i = 1; i <= N; i++) sum[i] = sum[i - 1] + mobius[i];
}
int main() {
sieve(maxn - 1);
int a, b, d, T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &a, &b, &d);
a /= d, b /= d;
int n = min(a, b);
ll ans = 0;
//敲黑板!整除分块。
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n, min(a / (a / l), b / (b / l)));
ans += (ll)(sum[r] - sum[l - 1]) * (ll)(a / l) * (b / l);
}
printf("%lld\n", ans);
}
return 0;
}
μ ( x ) = { 0 , ∃ α i ≥ 2. ( − 1 ) k , ∀ α i = 1. \mu(x)=\begin{cases}0,\exist\alpha_i \ge 2. \\ (-1)^k,\forall\alpha_i=1. \end{cases} μ(x)={0,∃αi≥2.(−1)k,∀αi=1.
ε ( n ) = ∑ d ∣ n μ ( d ) = { 1 , n = 1 0 , n > 1 . \varepsilon(n) = \sum\limits_{d|n}\mu(d) = \begin{cases}1,n=1\\0, n>1 \end{cases}. ε(n)=d∣n∑μ(d)={1,n=10,n>1.
设 f ( n ) , g ( n ) f(n),g(n) f(n),g(n) 为两个数论函数
如果有 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum_{d\mid n}g(d) f(n)=∑d∣ng(d),那么有 g ( n ) = ∑ d ∣ n μ ( d ) f ( n d ) g(n)=\sum_{d\mid n}\mu(d)f(\frac{n}{d}) g(n)=∑d∣nμ(d)f(dn)。
如果有 f ( n ) = ∑ n ∣ d g ( d ) f(n)=\sum_{n|d}g(d) f(n)=∑n∣dg(d),那么有 g ( n ) = ∑ n ∣ d μ ( d n ) f ( d ) g(n)=\sum_{n|d}\mu(\frac{d}{n})f(d) g(n)=∑n∣dμ(nd)f(d)。
形式一的证明:
∑ d ∣ n μ ( d ) f ( n d ) = ∑ d ∣ n μ ( d ) ∑ k ∣ n d g ( k ) = ∑ k ∣ n g ( k ) ∑ d ∣ n k μ ( d ) = g ( n ) \sum_{d\mid n}\mu(d)f(\frac{n}{d})=\sum_{d\mid n}\mu(d)\sum_{k\mid \frac{n}{d}}g(k)=\sum_{k\mid n}g(k)\sum_{d\mid \frac{n}{k}}\mu(d)=g(n) d∣n∑μ(d)f(dn)=d∣n∑μ(d)k∣dn∑g(k)=k∣n∑g(k)d∣kn∑μ(d)=g(n)
形式二的证明:
∑ n ∣ d μ ( d n ) f ( d ) = ∑ k = 1 + ∞ μ ( k ) f ( k n ) = ∑ k = 1 + ∞ μ ( k ) ∑ k n ∣ d g ( d ) = ∑ n ∣ d g ( d ) ∑ k ∣ d n μ ( k ) = ∑ n ∣ d g ( d ) ϵ ( d n ) = g ( n ) \begin{aligned} &\sum_{n|d}{\mu(\frac{d}{n})f(d)} \\ =& \sum_{k=1}^{+\infty}{\mu(k)f(kn)} = \sum_{k=1}^{+\infty}{\mu(k)\sum_{kn|d}{g(d)}} \\ =& \sum_{n|d}{g(d)\sum_{k|\frac{d}{n}}{\mu(k)}} = \sum_{n|d}{g(d)\epsilon(\frac{d}{n})} \\ =& g(n) \end{aligned} ===n∣d∑μ(nd)f(d)k=1∑+∞μ(k)f(kn)=k=1∑+∞μ(k)kn∣d∑g(d)n∣d∑g(d)k∣nd∑μ(k)=n∣d∑g(d)ϵ(nd)g(n)
思考1:
求 ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = 1 ] \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[(i,j)=1] i=1∑nj=1∑m[(i,j)=1].
∑ i = 1 n ∑ j = 1 m [ ( i , j ) = 1 ] = ∑ i = 1 n ∑ j = 1 m ∑ p ∣ ( i , j ) μ ( p ) = ∑ p = 1 n μ ( p ) ∑ p ∣ i ∑ p ∣ j [ p ∣ ( i , j ) ] = ∑ p = 1 n μ ( p ) ∑ p ∣ i [ p ∣ i ] ∑ p ∣ j [ p ∣ j ] ( i ′ = i / p , j ′ = j / p , n ′ = n / p , m ′ = m / p ) = ∑ p = 1 n μ ( p ) ∗ ( ∑ i ′ = 1 n ′ 1 ) ∗ ( ∑ j ′ = 1 m ′ 1 ) = ∑ p = 1 n μ ( p ) ∗ n ′ ∗ m ′ \begin{align} &\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[(i,j)=1]\\ =&\sum\limits_{i=1}^n\sum\limits_{j = 1}^m\sum\limits_{p\mid (i,j)}\mu(p)\\ =& \sum\limits_{p=1}^n \mu(p) \sum\limits_{p \mid i} \sum\limits_{p\mid j} [p \mid (i,j)] \\ =& \sum\limits_{p=1}^n \mu(p) \sum\limits_{p \mid i} [p \mid i] \sum\limits_{p\mid j} [p \mid j] \\ &(i' = i/p,\ j'=j/p,\ n' = n / p,\ m' = m / p)\\ =& \sum\limits_{p=1}^{n}\mu(p) *(\sum\limits_{i'=1}^{n'}1) * (\sum\limits_{j'=1}^{m'}1) \\ =&\sum\limits_{p=1}^n \mu(p)* n'* m' \end{align} =====i=1∑nj=1∑m[(i,j)=1]i=1∑nj=1∑mp∣(i,j)∑μ(p)p=1∑nμ(p)p∣i∑p∣j∑[p∣(i,j)]p=1∑nμ(p)p∣i∑[p∣i]p∣j∑[p∣j](i′=i/p, j′=j/p, n′=n/p, m′=m/p)p=1∑nμ(p)∗(i′=1∑n′1)∗(j′=1∑m′1)p=1∑nμ(p)∗n′∗m′
思考2:
GCD SUM
给出两个正整数 n n n 和 m m m,求 ∑ i = 1 n ∑ j = 1 m ( i , j ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(i,j) i=1∑nj=1∑m(i,j)
∑ i = 1 n ∑ j = 1 m ( i , j ) = ∑ p = 1 n ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = p ] ∗ p = ∑ p = 1 n p ∑ i ′ = 1 n / p ∑ j ′ = 1 m / p [ ( i , j ) = 1 ] \begin{align} &\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(i,j) \\ =& \sum\limits_{p=1}^n \sum\limits_{i=1}^{n}\sum\limits_{j=1}^m [(i,j)=p]*p\\ =& \sum\limits_{p=1}^n p \sum\limits_{i'=1}^{n/p}\sum\limits_{j'=1}^{m/p} [(i,j)=1] \end{align} ==i=1∑nj=1∑m(i,j)p=1∑ni=1∑nj=1∑m[(i,j)=p]∗pp=1∑npi′=1∑n/pj′=1∑m/p[(i,j)=1]
题意:给定一个整数 N N N,请你求出 ∑ i = 1 N g c d ( i , N ) \sum\limits_{i = 1}^Ngcd(i,N) i=1∑Ngcd(i,N) 的值。
对于每一个 d d d,把它质因数分解后,都可以找到对应的 p α p^{\alpha} pα. 而 ∏ i = 1 k p i α \prod\limits_{i=1}^{k}p_i^{\alpha} i=1∏kpiα 和 ∏ i = 1 k ( 1 − 1 p i ) \prod\limits_{i=1}^{k}(1-\frac{1}{p_i}) i=1∏k(1−pi1) 一一对应。因此就写成了上面那种形式。即 ∑ d ∣ n ∏ i = 1 k ( 1 − 1 p i ) = ∏ i = 1 k ( 1 + α i ( 1 − 1 p i ) ) \sum\limits_{d|n}\prod\limits_{i=1}^{k}(1-\frac{1}{p_i}) = \prod\limits_{i=1}^{k}(1+\alpha_i(1-\frac{1}{p_i})) d∣n∑i=1∏k(1−pi1)=i=1∏k(1+αi(1−pi1))
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int main() {
ll N;
cin >> N;
ll res = N;
for (ll i = 2; i <= N / i; i++) {
if (N % i == 0) {
ll a = 0, p = i;
while (N % p == 0) a++, N /= p;
res = res * (p + a * p - a) / p;
}
}
if (N > 1) res = res * (N + N - 1) / N;
cout << res << endl;
return 0;
}
一个数 a a a,如果不是 p p p 的倍数且模 p p p 同余于某个数的平方,则称 a a a 为模 p p p 的 二次剩余。而一个不是 p p p 的倍数的数 b b b,不同余于任何数的平方,则称 b b b 为模 p p p 的 非二次剩余。
对二次剩余求解,也就是对常数 a a a 解下面的这个方程:
x 2 ≡ a ( m o d p ) x^2 \equiv a \pmod p x2≡a(modp)
通俗一些,可以认为是求模意义下的开方。这里只讨论 p \boldsymbol{p} p 为奇素数 的求解方法,将会使用 Cipolla 算法。
对于 x 2 ≡ n ( m o d p ) x^2 \equiv n \pmod p x2≡n(modp),能满足" n n n 是模 p p p 的二次剩余"的 n n n 一共有 p − 1 2 \frac{p-1}{2} 2p−1 个(0 不包括在内),非二次剩余有 p − 1 2 \frac{p-1}{2} 2p−1 个。
( n p ) = { 1 , p ∤ n 且 n 是 p 的二次剩余 − 1 , p ∤ n 且 n 不是 p 的二次剩余 0 , p ∣ n \left(\frac{n}{p}\right)=\begin{cases} 1,\,&p\nmid n \text{且}n\text{是}p\text{的二次剩余}\\ -1,\,&p\nmid n \text{且}n\text{不是}p\text{的二次剩余}\\ 0,\,&p\mid n \end{cases} (pn)=⎩ ⎨ ⎧1,−1,0,p∤n且n是p的二次剩余p∤n且n不是p的二次剩余p∣n
( n p ) ≡ n p − 1 2 ( m o d p ) \left(\frac{n}{p}\right)\equiv n^{\frac{p-1}{2}}\pmod p (pn)≡n2p−1(modp)
若 n n n 是二次剩余,当且仅当 n p − 1 2 ≡ 1 ( m o d p ) n^{\frac{p-1}{2}}\equiv 1\pmod p n2p−1≡1(modp)。
若 n n n 是非二次剩余,当且仅当 n p − 1 2 ≡ − 1 ( m o d p ) n^{\frac{p-1}{2}}\equiv -1\pmod p n2p−1≡−1(modp)。
找到一个数 a a a 满足 a 2 − n a^2-n a2−n 是 非二次剩余,这里通过生成随机数再检验的方法来实现,由于非二次剩余的数量为 p − 1 2 \frac{p-1}{2} 2p−1,接近 p 2 \frac{p}{2} 2p,所以期望约 2 2 2 次就可以找到这个数。
扩域,这里定义 i 2 = a 2 − n i^2=a^2-n i2=a2−n,于是就可以将所有的数表达为 A + B i A+Bi A+Bi 的形式,这里的 A A A 和 B B B 都是模意义下的数,类似复数中的实部和虚部。
在有了 i i i 和 a a a 后可以直接得到答案, x 2 ≡ n ( m o d p ) x^2\equiv n\pmod p x2≡n(modp) 的解为 ( a + i ) p + 1 2 (a+i)^{\frac{p+1}{2}} (a+i)2p+1。
O ( log p ) O(\log p) O(logp)
给出 N , p N, p N,p,求解方程
x 2 ≡ N ( m o d p ) x^2 \equiv N(\bmod p) x2≡N(modp)
多组数据。
保证 p 是奇素数。 1 ≤ T ≤ 10000 1≤T≤10000 1≤T≤10000, 1 ≤ N , p ≤ 1 0 9 1\le N, p\leq 10^9 1≤N,p≤109
#include
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> num;
mt19937 rnd(0);
ll n, p, w;
num mul(num& a, num& b, ll p)
{
num res;
res.x = ((a.x * b.x % p + a.y * b.y % p * w % p) + p) % p;
res.y = ((a.x * b.y % p + a.y * b.x % p) % p + p) % p;
return res;
}
ll pow_real(ll x, ll n, ll p)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % p;
x = x * x % p;
n >>= 1;
}
return res;
}
ll pow_imag(num x, ll n, ll p)
{
num res = {1, 0};
while(n)
{
if(n & 1) res = mul(res, x, p);
x = mul(x, x, p);
n >>= 1;
}
return res.x;
}
ll cipolla(ll n, ll p)
{
n %= p;
if(n == 0) return 0;
if(p == 2) return n;
if(pow_real(n, (p - 1) / 2, p) == p - 1) return -1;
ll a;
while(1)
{
a = rnd() % p;
w = ((a * a % p - n) % p + p) % p;
if(pow_real(w, (p - 1) / 2, p) == p - 1) break;
}
num x = {a, 1};
return pow_imag(x, (p + 1) / 2, p);
}
阶:由欧拉定理可知,对 a ∈ Z a\in \mathbb{Z} a∈Z, m ∈ N ∗ m\in\mathbb{N}^{*} m∈N∗,若 gcd ( a , m ) = 1 \gcd(a,m)=1 gcd(a,m)=1,则 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)}\equiv 1\pmod m aφ(m)≡1(modm)。
因此满足同余式 a n ≡ 1 ( m o d m ) a^n \equiv 1 \pmod m an≡1(modm) 的最小正整数 n n n 存在,这个 n n n 称作 a a a 模 m m m 的阶,记作 δ m ( a ) \delta_m(a) δm(a)。
性质 1 1 1: a , a 2 , ⋯ , a δ m ( a ) a,a^2,\cdots,a^{\delta_m(a)} a,a2,⋯,aδm(a) 模 m m m 两两不同余。
性质 2 2 2:若 a n ≡ 1 ( m o d m ) a^n \equiv 1 \pmod m an≡1(modm),则 δ m ( a ) ∣ n \delta_m(a)\mid n δm(a)∣n。
性质 3 3 3:设 m ∈ N ∗ m\in\mathbb{N}^{*} m∈N∗, a , b ∈ Z a,b\in\mathbb{Z} a,b∈Z, gcd ( a , m ) = gcd ( b , m ) = 1 \gcd(a,m)=\gcd(b,m)=1 gcd(a,m)=gcd(b,m)=1,则
δ m ( a b ) = δ m ( a ) δ m ( b ) \delta_m(ab)=\delta_m(a)\delta_m(b) δm(ab)=δm(a)δm(b)
的充分必要条件是
gcd ( δ m ( a ) , δ m ( b ) ) = 1 \gcd\big(\delta_m(a),\delta_m(b)\big)=1 gcd(δm(a),δm(b))=1
性质4:设 k ∈ N k \in \mathbb{N} k∈N, m ∈ N ∗ m\in \mathbb{N}^{*} m∈N∗, a ∈ Z a\in\mathbb{Z} a∈Z, gcd ( a , m ) = 1 \gcd(a,m)=1 gcd(a,m)=1,则
δ m ( a k ) = δ m ( a ) gcd ( δ m ( a ) , k ) \delta_m(a^k)=\dfrac{\delta_m(a)}{\gcd\big(\delta_m(a),k\big)} δm(ak)=gcd(δm(a),k)δm(a)
原根:设 m ∈ N ∗ m \in \mathbb{N}^{*} m∈N∗, a ∈ Z a\in \mathbb{Z} a∈Z。若 gcd ( a , m ) = 1 \gcd(a,m)=1 gcd(a,m)=1,且 δ m ( a ) = φ ( m ) \delta_m(a)=\varphi(m) δm(a)=φ(m),则称 a a a 为模 m m m 的原根。
原根判定定理:若一个数 g g g 是模 m m m 的原根,则有对于 φ ( m ) \varphi(m) φ(m) 任何大于 1 1 1 且不为自身的因数 p p p,都有 g φ ( m ) / p ≢ 1 ( m o d m ) g^{\varphi(m)/p}\not\equiv 1\pmod m gφ(m)/p≡1(modm)。实际上,只需要判断所有
原根个数:若一个数 m m m 有原根,则它原根的个数为 φ ( φ ( m ) ) \varphi(\varphi(m)) φ(φ(m))。
证明:
若 m m m 有原根 g g g,则:
δ m ( g k ) = δ m ( g ) gcd ( δ m ( g ) , k ) = φ ( m ) gcd ( φ ( m ) , k ) \delta_m(g^k)=\dfrac{\delta_m(g)}{\gcd\big(\delta_m(g),k\big)}=\dfrac{\varphi(m)}{\gcd\big(\varphi(m),k\big)} δm(gk)=gcd(δm(g),k)δm(g)=gcd(φ(m),k)φ(m)
所以若 gcd ( k , φ ( m ) ) = 1 \gcd\big(k,\varphi(m)\big)=1 gcd(k,φ(m))=1,则有: δ m ( g k ) = φ ( m ) \delta_m(g^k)=\varphi(m) δm(gk)=φ(m),即 g k g^k gk 也是模 m m m 的原根。
而满足 gcd ( φ ( m ) , k ) = 1 \gcd\big(\varphi(m),k\big)=1 gcd(φ(m),k)=1 且 1 ≤ k ≤ φ ( m ) 1\leq k \leq \varphi(m) 1≤k≤φ(m) 的 k k k 有 φ ( φ ( m ) ) \varphi(\varphi(m)) φ(φ(m)) 个。所以原根就有 φ ( φ ( m ) ) \varphi(\varphi(m)) φ(φ(m)) 个。
原根存在定理:一个数 m m m 存在原根当且仅当 m = 2 , 4 , p α , 2 p α m=2,4,p^{\alpha},2p^{\alpha} m=2,4,pα,2pα,其中 p p p 为奇素数, α ∈ N ∗ \alpha\in \mathbb{N}^{*} α∈N∗。
给定整数 n n n,求它的所有原根。
为了减小你的输出量,给出输出参数 d d d,设 n n n 的所有原根有 c c c 个,从小到大分别为 g 1 , … , g c g_1,\ldots,g_c g1,…,gc,你只需要依次输出 g d , g 2 d , … , g ⌊ c d ⌋ × d g_d,g_{2d},\ldots,g_{\lfloor\frac{c}{d}\rfloor\times d} gd,g2d,…,g⌊dc⌋×d。
暴力找最小原根: O ( n 0.25 ∗ log 2 φ ( n ) ) O\big(n^{0.25} * \log^2 \varphi(n)\big) O(n0.25∗log2φ(n));
n n n 的最小原根,设为 g g g,则 n n n 的所有原根可以由 g g g 的若干次乘方得到。具体地,若 n n n 存在原根,则其原根个数为 φ ( φ ( n ) ) \varphi(\varphi(n)) φ(φ(n)),每一个原根都形如 g k g^k gk 的形式,要求满足 gcd ( k , φ ( n ) ) = 1 \gcd(k,\varphi(n))=1 gcd(k,φ(n))=1。把所有原根排个序输出即可.
#include
using namespace std;
typedef long long ll;
const int N = 1000010;
int prime[N], phi[N], cnt;
int st[N];
void init(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!st[i]) st[i] = prime[cnt++] = i, phi[i] = i - 1;
for(int j = 0; prime[j] <= n / i; j++)
{
st[i * prime[j]] = prime[j];
if(i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
vector<int> divisor(int n)
{
vector<int> res;
while(n > 1)
{
int t = st[n];
res.push_back(t);
while(n % t == 0) n /= t;
}
return res;
}
ll mod_pow(ll x, ll n, ll mod)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
vector<int> ans;
void solve(ll n)
{
if(n == 2){
ans.push_back(1);
return;
}
ll g = -1;
vector<int> div = divisor(phi[n]);
for(int i = 1; i <= n; i++)
{
if(mod_pow(i, phi[n], n) != 1) continue;
bool flag = true;
for(auto p : div)
{
if(mod_pow(i, phi[n] / p, n) == 1)
{
flag = false;
break;
}
}
if(flag)
{
g = i;
break;
}
}
if(g == -1) return;
if(g == 1) ans.push_back(1);
else
{
ll tmp = g;
for(int i = 1; i <= phi[n]; i++, tmp = tmp * g % n)
{
if(__gcd(i, phi[n]) == 1) ans.push_back(tmp);
}
}
}
int main()
{
init(N - 1);
int T;
scanf("%d", &T);
while(T--)
{
ans.clear();
int n, d;
scanf("%d%d", &n, &d);
solve(n);
printf("%d\n", (int)ans.size());
sort(ans.begin(), ans.end());
for(int i = d - 1; i < ans.size(); i += d)
{
printf("%d ", ans[i]);
}
printf("\n");
}
return 0;
}
给定正整数 a , p , b a,p,b a,p,b,数据保证 a a a 和 p p p 互质。求满足 a x ≡ b ( m o d p ) a^x\equiv b(mod\ p) ax≡b(mod p) 的最小非负整数 x x x。
令 x = A ⌈ p ⌉ − B x = A \left \lceil \sqrt p \right \rceil - B x=A⌈p⌉−B,其中 0 ≤ A , B ≤ ⌈ p ⌉ 0\le A,B \le \left \lceil \sqrt p \right \rceil 0≤A,B≤⌈p⌉,则有 a A ⌈ p ⌉ − B ≡ b ( m o d p ) a^{A\left \lceil \sqrt p \right \rceil -B} \equiv b \pmod p aA⌈p⌉−B≡b(modp),稍加变换,则有 a A ⌈ p ⌉ ≡ b a B ( m o d p ) a^{A\left \lceil \sqrt p \right \rceil} \equiv ba^B \pmod p aA⌈p⌉≡baB(modp)。
我们已知的是 a , b a,b a,b,所以我们可以先算出等式右边的 b a B ba^B baB 的所有取值,枚举 B B B,用 hash
/map
存下来,然后逐一计算 a A ⌈ p ⌉ a^{A\left \lceil \sqrt p \right \rceil} aA⌈p⌉,枚举 A A A,寻找是否有与之相等的 b a B ba^B baB,从而我们可以得到所有的 x x x, x = A ⌈ p ⌉ − B x=A \left \lceil \sqrt p \right \rceil - B x=A⌈p⌉−B。
注意到 A , B A,B A,B 均小于 ⌈ p ⌉ \left \lceil \sqrt p \right \rceil ⌈p⌉,所以时间复杂度为 Θ ( p ) \Theta\left (\sqrt p\right ) Θ(p),用 map
则多一个 log \log log。
代码:
#include
using namespace std;
typedef long long ll;
ll a, b, p;
ll bsgs() {
if (1 % p == b % p) return 0;
ll k = sqrt(p) + 1;
unordered_map<ll, ll> hash;
for (ll i = 0, j = b % p; i < k; i++) {
hash[j] = i;
j = j * a % p;
}
ll ak = 1;
for (int i = 0; i < k; i++) ak = ak * a % p;
for (ll i = 1, j = ak; i <= k; i++) {
if (hash.count(j)) return k * i - hash[j];
j = j * ak % p;
}
return -1;
}
int main() {
while (cin >> a >> p >> b, a || p || b) {
ll res = bsgs();
if (res == -1) cout << "No Solution" << endl;
else cout << res << endl;
}
return 0;
}
代码(这道题卡常数,所以开了O2优化):
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int INF = 1e8;
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (!b) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
ll bsgs(ll a, ll b, ll p) {
if (1 % p == b % p) return 0;
ll k = sqrt(p) + 1;
unordered_map<ll, ll> hash;
for (ll i = 0, j = b % p; i < k; i++) {
hash[j] = i;
j = j * a % p;
}
ll ak = 1;
for (int i = 0; i < k; i++) ak = ak * a % p;
for (ll i = 1, j = ak; i <= k; i++) {
if (hash.count(j)) return k * i - hash[j];
j = j * ak % p;
}
return -INF;
}
ll exbsgs(ll a, ll b, ll p) {
b = (b % p + p) % p; //防止b是负数
if (1 % p == b % p) return 0;
ll x, y;
ll d = exgcd(a, p, x, y); //求最大公约数
if (d > 1) {
if (b % d) return -INF; //无解
exgcd(a / d, p / d, x, y);
return exbsgs(a, b / d * x % (p / d), p / d) + 1;
}
return bsgs(a, b, p);
}
int main() {
ll a, b, p;
while (cin >> a >> p >> b, a || p || b) {
ll res = exbsgs(a, b, p);
if (res < 0) cout << "No Solution" << endl;
else cout << res << endl;
}
return 0;
}
设模 m m m 有原根 g g g,则 { 1 , g , g 2 , . . . , g φ ( m ) − 1 } \{1,g,g^2,...,g^{\varphi(m)-1}\} {1,g,g2,...,gφ(m)−1} 为模 m m m 的缩系. 所以对于每个与 m m m 互素的整数 a a a,必存在唯一的整数 k k k,使得
a ≡ g k ( m o d m ) , 0 ≤ k ≤ φ ( m ) − 1 a \equiv g^k \pmod m, 0 \le k \le \varphi(m) - 1 a≡gk(modm),0≤k≤φ(m)−1
上述的 k k k 叫做 a a a (对于原根 g g g) 模 m m m 的指数,表示成 k = i n d g a k = ind_ga k=indga. 指数也叫做离散对数.
给出三个正整数 p , k , a p,k,a p,k,a,其中 p p p 是素数,保证有解,输出所有满足 x k ≡ a ( m o d p ) x^k \equiv a \pmod p xk≡a(modp) 且 0 ≤ x ≤ p − 1 0\le x \le p-1 0≤x≤p−1 的整数 x x x.
2 ≤ p ≤ 1 0 9 , 2 ≤ k ≤ 1 0 5 , 0 ≤ a < p 2 \le p \le 10^9, 2 \le k \le 10^5, 0 \le a < p 2≤p≤109,2≤k≤105,0≤a<p.
我们令 x = g c x=g^c x=gc, g g g 是 p p p 的原根(我们一定可以找到这个 g g g 和 c c c),问题转化为求解 ( g c ) a ≡ b ( m o d p ) (g^c)^a \equiv b \pmod p (gc)a≡b(modp)。稍加变换,得到
( g a ) c ≡ b ( m o d p ) (g^a)^c \equiv b \pmod p (ga)c≡b(modp)
于是就转换成了我们熟知的 BSGS 的基本模型了,可以在 O ( p ) O(\sqrt p) O(p) 解出 c c c,这样可以得到原方程的一个特解 x 0 ≡ g c ( m o d p ) x_0\equiv g^c\pmod p x0≡gc(modp)。
我们仍令 x = g c x=g^c x=gc,并且设 b = g t b=g^t b=gt,于是我们得到
g a c ≡ g t ( m o d p ) g^{ac}\equiv g^t\pmod p gac≡gt(modp)
方程两边同时取离散对数得到
a c ≡ t ( m o d φ ( p ) ) ac\equiv t\pmod{\varphi(p)} ac≡t(modφ(p))
我们可以通过 BSGS 求解 g t ≡ b ( m o d p ) g^t\equiv b\pmod p gt≡b(modp) 得到 t t t,于是这就转化成了一个线性同余方程的问题。这样也可以解出 c c c,求出 x x x 的一个特解 x 0 ≡ g c ( m o d p ) x_0\equiv g^c\pmod p x0≡gc(modp)。